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.
Files changed (77) hide show
  1. package/.github/workflows/publish.yml +32 -0
  2. package/README.md +205 -89
  3. package/README.zh-cn.md +221 -68
  4. package/README.zh-tw.md +221 -68
  5. package/bower.json +6 -9
  6. package/demo/10000-nodes.html +139 -0
  7. package/demo/ajax-datasource.html +46 -45
  8. package/demo/bottom2top.html +12 -9
  9. package/demo/center-on-selected-node.html +21 -17
  10. package/demo/color-coded.html +12 -9
  11. package/demo/css/style.css +28 -0
  12. package/demo/custom-icons.html +12 -9
  13. package/demo/custom-template.html +12 -9
  14. package/demo/data-prop-compact.html +11 -8
  15. package/demo/data-prop-hybrid.html +79 -50
  16. package/demo/default-collapsed.html +12 -9
  17. package/demo/drag-drop-hybrid-chart.html +17 -14
  18. package/demo/drag-drop.html +21 -16
  19. package/demo/edit-chart.html +148 -94
  20. package/demo/edit-hybrid-chart.html +178 -135
  21. package/demo/expand-to-given-depth.html +30 -22
  22. package/demo/export-chart-pdf.html +12 -9
  23. package/demo/export-chart-with-pictures.html +19 -12
  24. package/demo/export-chart.html +12 -9
  25. package/demo/family-tree.html +100 -0
  26. package/demo/familytree-custom-properties.html +108 -0
  27. package/demo/filter-node.html +104 -57
  28. package/demo/get-hierarchy.html +17 -12
  29. package/demo/get-related-nodes.html +27 -17
  30. package/demo/index.html +7 -4
  31. package/demo/integrate-map.html +32 -27
  32. package/demo/left2right.html +12 -9
  33. package/demo/level-offset.html +16 -15
  34. package/demo/link-node.html +12 -9
  35. package/demo/local-datasource.html +11 -8
  36. package/demo/minimap.html +224 -0
  37. package/demo/nodes-of-different-widths.html +11 -8
  38. package/demo/ondemand-loading-data.html +88 -91
  39. package/demo/option-createNode.html +31 -20
  40. package/demo/pan-zoom.html +20 -11
  41. package/demo/reload-data.html +17 -14
  42. package/demo/report-path.html +59 -29
  43. package/demo/responsive-design.html +14 -11
  44. package/demo/right2left.html +12 -9
  45. package/demo/toggle-sibs-resp.html +12 -9
  46. package/demo/ul-datasource.html +11 -8
  47. package/demo/vertical-level.html +12 -9
  48. package/dist/css/{jquery.orgchart.css → orgchart.css} +210 -78
  49. package/dist/css/orgchart.min.css +1 -0
  50. package/dist/js/orgchart.js +4101 -0
  51. package/dist/js/orgchart.min.js +2 -0
  52. package/dist/js/orgchart.min.js.map +1 -0
  53. package/gulpfile.js +37 -26
  54. package/package.json +32 -32
  55. package/src/css/{jquery.orgchart.css → orgchart.css} +210 -78
  56. package/src/js/orgchart.js +4101 -0
  57. package/test/cypress/e2e/demo-interactions.cy.js +91 -0
  58. package/test/cypress/e2e/demo-rendering.cy.js +100 -0
  59. package/test/cypress/e2e/drag-drop-hybrid-chart.cy.js +22 -0
  60. package/test/cypress/e2e/edit-chart.cy.js +20 -0
  61. package/test/cypress/e2e/level-offset.cy.js +21 -0
  62. package/test/cypress/e2e/minimap.cy.js +66 -0
  63. package/test/cypress/e2e/pan-zoom.cy.js +165 -0
  64. package/test/cypress/e2e/report-path.cy.js +20 -0
  65. package/test/cypress/e2e/vertical-vevel.cy.js +5 -3
  66. package/test/e2e/local-datasource/test.js +1 -1
  67. package/test/integration/init-tests.js +168 -62
  68. package/test/integration/tests.js +1034 -56
  69. package/test/unit/lifecycle-builders-export.tests.js +1426 -0
  70. package/test/unit/minimap.tests.js +210 -0
  71. package/test/unit/tests.js +2372 -252
  72. package/demo/multiple-layers.html +0 -128
  73. package/dist/css/jquery.orgchart.min.css +0 -1
  74. package/dist/js/jquery.orgchart.js +0 -1686
  75. package/dist/js/jquery.orgchart.min.js +0 -5
  76. package/dist/js/jquery.orgchart.min.js.map +0 -1
  77. package/src/js/jquery.orgchart.js +0 -1686
@@ -0,0 +1,4101 @@
1
+ /*
2
+ * JavaScript OrgChart Component
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
+ module.exports = factory(window, document);
16
+ } else {
17
+ window.OrgChart = factory(window, document);
18
+ }
19
+ }(function (window, document, undefined) {
20
+ const elementDataStore = new WeakMap();
21
+ const getWindow = () => (typeof globalThis !== 'undefined' && globalThis.window ? globalThis.window : window);
22
+ const getDocument = () => {
23
+ const currentWindow = getWindow();
24
+ return currentWindow && currentWindow.document
25
+ ? currentWindow.document
26
+ : (typeof globalThis !== 'undefined' && globalThis.document ? globalThis.document : document);
27
+ };
28
+
29
+ const isElement = (value) => value && (value.nodeType === 1 || value.nodeType === 9 || value === getWindow());
30
+ const isNode = (value) => value && typeof value.nodeType === 'number';
31
+ const isNodeList = (value) => value && typeof value.length === 'number' && typeof value !== 'string' && !isNode(value);
32
+ const isHtmlString = (value) => typeof value === 'string' && value.trim().startsWith('<') && value.trim().endsWith('>');
33
+ const isPlainObject = (value) => Object.prototype.toString.call(value) === '[object Object]';
34
+ const unique = (elements) => Array.from(new Set(elements.filter(Boolean)));
35
+
36
+ function getElement(input, context) {
37
+ return normalizeInput(input, context || getDocument())[0] || null;
38
+ }
39
+
40
+ function getElements(input, context) {
41
+ return normalizeInput(input, context || getDocument());
42
+ }
43
+
44
+ function mergeObjects() {
45
+ return Object.assign.apply(Object, [{}].concat(Array.from(arguments)));
46
+ }
47
+
48
+ function forEachValue(collection, callback) {
49
+ if (Array.isArray(collection) || isNodeList(collection)) {
50
+ for (let index = 0; index < collection.length; index += 1) {
51
+ if (callback.call(collection[index], index, collection[index]) === false) {
52
+ break;
53
+ }
54
+ }
55
+ return collection;
56
+ }
57
+ Object.keys(collection || {}).some((key) => callback.call(collection[key], key, collection[key]) === false);
58
+ return collection;
59
+ }
60
+
61
+ function createEventLike(type) {
62
+ return {
63
+ type,
64
+ defaultPrevented: false,
65
+ preventDefault: function () {
66
+ this.defaultPrevented = true;
67
+ },
68
+ isDefaultPrevented: function () {
69
+ return this.defaultPrevented;
70
+ }
71
+ };
72
+ }
73
+
74
+ function normalizeSelector(selector) {
75
+ if (!selector) {
76
+ return selector;
77
+ }
78
+ return selector.replace(/:not\("([^"]+)"\)/g, ':not($1)');
79
+ }
80
+
81
+ function parseSelector(selector) {
82
+ const normalizedSelector = normalizeSelector(selector || '*');
83
+ const meta = {
84
+ selector: normalizedSelector,
85
+ first: false,
86
+ last: false,
87
+ gt: null,
88
+ hidden: false,
89
+ visible: false
90
+ };
91
+ meta.selector = meta.selector.replace(/:first\b/g, function () {
92
+ meta.first = true;
93
+ return '';
94
+ });
95
+ meta.selector = meta.selector.replace(/:last\b/g, function () {
96
+ meta.last = true;
97
+ return '';
98
+ });
99
+ meta.selector = meta.selector.replace(/:gt\((\d+)\)/g, function (_, index) {
100
+ meta.gt = Number(index);
101
+ return '';
102
+ });
103
+ meta.selector = meta.selector.replace(/:hidden\b/g, function () {
104
+ meta.hidden = true;
105
+ return '';
106
+ });
107
+ meta.selector = meta.selector.replace(/:visible\b/g, function () {
108
+ meta.visible = true;
109
+ return '';
110
+ });
111
+ meta.selector = meta.selector.trim() || '*';
112
+ return meta;
113
+ }
114
+
115
+ function isVisibleElement(element) {
116
+ if (!element || element.nodeType !== 1) {
117
+ return false;
118
+ }
119
+ const style = getWindow().getComputedStyle(element);
120
+ return style.display !== 'none' && style.visibility !== 'hidden';
121
+ }
122
+
123
+ function matchesSelector(element, selector) {
124
+ if (!selector || selector === '*') {
125
+ return true;
126
+ }
127
+ if (!element || element.nodeType !== 1) {
128
+ return false;
129
+ }
130
+ try {
131
+ return element.matches(selector);
132
+ } catch (error) {
133
+ return false;
134
+ }
135
+ }
136
+
137
+ function filterBySelector(elements, selector) {
138
+ const meta = parseSelector(selector);
139
+ let filtered = elements.filter((element) => matchesSelector(element, meta.selector));
140
+ if (meta.hidden) {
141
+ filtered = filtered.filter((element) => !isVisibleElement(element));
142
+ }
143
+ if (meta.visible) {
144
+ filtered = filtered.filter((element) => isVisibleElement(element));
145
+ }
146
+ if (meta.gt !== null) {
147
+ filtered = filtered.filter((_, index) => index > meta.gt);
148
+ }
149
+ if (meta.first) {
150
+ filtered = filtered.slice(0, 1);
151
+ }
152
+ if (meta.last) {
153
+ filtered = filtered.slice(-1);
154
+ }
155
+ return filtered;
156
+ }
157
+
158
+ function createNodesFromHtml(html) {
159
+ const template = getDocument().createElement('template');
160
+ template.innerHTML = html.trim();
161
+ return Array.from(template.content.childNodes);
162
+ }
163
+
164
+ function toDataKey(key) {
165
+ return String(key).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
166
+ }
167
+
168
+ function getStoredState(element) {
169
+ if (!elementDataStore.has(element)) {
170
+ elementDataStore.set(element, {});
171
+ }
172
+ return elementDataStore.get(element);
173
+ }
174
+
175
+ function setState(element, key, value) {
176
+ const store = getStoredState(element);
177
+ store[toDataKey(key)] = value;
178
+ }
179
+
180
+ function getState(element, key) {
181
+ const store = getStoredState(element);
182
+ const normalizedKey = toDataKey(key);
183
+ if (Object.prototype.hasOwnProperty.call(store, normalizedKey)) {
184
+ return store[normalizedKey];
185
+ }
186
+ return undefined;
187
+ }
188
+
189
+ function getDatasetSnapshot(element) {
190
+ return element.dataset ? Object.assign({}, element.dataset) : {};
191
+ }
192
+
193
+ function normalizeInput(input, context) {
194
+ if (input === null || input === undefined) {
195
+ return [];
196
+ }
197
+ if (Array.isArray(input)) {
198
+ return unique(input.flatMap((item) => normalizeInput(item, context)));
199
+ }
200
+ if (isNode(input)) {
201
+ return [input];
202
+ }
203
+ if (isNodeList(input)) {
204
+ return Array.from(input).flatMap((item) => normalizeInput(item, context));
205
+ }
206
+ if (typeof input === 'string') {
207
+ if (isHtmlString(input)) {
208
+ return createNodesFromHtml(input);
209
+ }
210
+ const currentDocument = getDocument();
211
+ const parents = normalizeInput(context || currentDocument, currentDocument);
212
+ const selected = parents.length
213
+ ? parents.flatMap((parent) => {
214
+ if (parent.nodeType !== 1 && parent.nodeType !== 9) {
215
+ return [];
216
+ }
217
+ return filterBySelector(Array.from(parent.querySelectorAll(parseSelector(input).selector)), input);
218
+ })
219
+ : filterBySelector(Array.from(currentDocument.querySelectorAll(parseSelector(input).selector)), input);
220
+ return unique(selected);
221
+ }
222
+ return [];
223
+ }
224
+
225
+ function toNodeArray(content) {
226
+ if (content instanceof DocumentFragment) {
227
+ return Array.from(content.childNodes);
228
+ }
229
+ if (Array.isArray(content)) {
230
+ return content.flatMap((item) => toNodeArray(item));
231
+ }
232
+ if (isNode(content)) {
233
+ return [content];
234
+ }
235
+ if (typeof content === 'string') {
236
+ return isHtmlString(content) ? createNodesFromHtml(content) : [getDocument().createTextNode(content)];
237
+ }
238
+ return [];
239
+ }
240
+
241
+ function cloneNodesForTarget(nodes, needsClone) {
242
+ return nodes.map((node) => (needsClone ? node.cloneNode(true) : node));
243
+ }
244
+
245
+ function createTriggeredEvent(eventLike, extraData) {
246
+ const type = typeof eventLike === 'string' ? eventLike : eventLike.type;
247
+ const event = new (getWindow().CustomEvent)(type, {
248
+ bubbles: true,
249
+ cancelable: true,
250
+ detail: extraData
251
+ });
252
+ event.originalEvent = event;
253
+ event.data = extraData;
254
+ event.delegateTarget = null;
255
+ return event;
256
+ }
257
+
258
+ function clampValue(value, min, max) {
259
+ return Math.min(Math.max(value, min), max);
260
+ }
261
+
262
+ function isTransparentColor(colorValue) {
263
+ const normalizedColor = (colorValue || '').replace(/\s+/g, '').toLowerCase();
264
+
265
+ return !normalizedColor
266
+ || normalizedColor === 'transparent'
267
+ || normalizedColor === 'rgba(0,0,0,0)'
268
+ || normalizedColor === 'hsla(0,0%,0%,0)';
269
+ }
270
+
271
+ function toReadableMinimapColor(colorValue) {
272
+ const rgbaMatch = /^rgba?\(([^)]+)\)$/i.exec((colorValue || '').trim());
273
+ const darkenFactor = 0.68;
274
+ let channelValues;
275
+
276
+ if (!rgbaMatch) {
277
+ return colorValue;
278
+ }
279
+
280
+ channelValues = rgbaMatch[1].split(',').slice(0, 3).map(function (value) {
281
+ return clampValue(Math.round(parseFloat(value.trim()) * darkenFactor), 0, 255);
282
+ });
283
+
284
+ if (channelValues.some(function (value) { return Number.isNaN(value); })) {
285
+ return colorValue;
286
+ }
287
+
288
+ return 'rgb(' + channelValues.join(', ') + ')';
289
+ }
290
+
291
+ function getMinimapNodeColor(nodeElement) {
292
+ const fallbackColor = 'rgba(217, 83, 79, 0.8)';
293
+ const titleElement = nodeElement ? nodeElement.querySelector('.title') : null;
294
+ const primaryColor = titleElement ? getWindow().getComputedStyle(titleElement).backgroundColor : '';
295
+ const secondaryColorCandidates = [
296
+ nodeElement,
297
+ titleElement,
298
+ nodeElement ? nodeElement.querySelector('.content') : null
299
+ ].flatMap(function (element) {
300
+ const style = element ? getWindow().getComputedStyle(element) : null;
301
+
302
+ if (!style) {
303
+ return [];
304
+ }
305
+
306
+ return [
307
+ style.backgroundColor,
308
+ style.borderTopColor,
309
+ style.borderRightColor,
310
+ style.borderBottomColor,
311
+ style.borderLeftColor
312
+ ];
313
+ });
314
+
315
+ return toReadableMinimapColor((!isTransparentColor(primaryColor) && primaryColor) || secondaryColorCandidates.find(function (color) {
316
+ return !isTransparentColor(color);
317
+ }) || fallbackColor);
318
+ }
319
+
320
+ const MINIMAP_NODE_HEIGHT = 18;
321
+
322
+ function renderMinimapNodes(chartElement, minimapContentElement, chartRect, chartScale) {
323
+ const fragment = getDocument().createDocumentFragment();
324
+
325
+ Array.from(chartElement.querySelectorAll('.node')).filter(function (nodeElement) {
326
+ return isVisibleElement(nodeElement);
327
+ }).forEach(function (nodeElement) {
328
+ const nodeRect = nodeElement.getBoundingClientRect();
329
+ const minimapNodeElement = getDocument().createElement('div');
330
+ const nodeTop = (nodeRect.top - chartRect.top) / chartScale;
331
+
332
+ minimapNodeElement.className = 'orgchart-minimap-node';
333
+ minimapNodeElement.style.left = Math.max((nodeRect.left - chartRect.left) / chartScale, 0) + 'px';
334
+ minimapNodeElement.style.top = Math.max(nodeTop + ((nodeRect.height / chartScale - MINIMAP_NODE_HEIGHT) / 2), 0) + 'px';
335
+ minimapNodeElement.style.width = Math.max(nodeRect.width / chartScale, 6) + 'px';
336
+ minimapNodeElement.style.height = MINIMAP_NODE_HEIGHT + 'px';
337
+ minimapNodeElement.style.backgroundColor = getMinimapNodeColor(nodeElement);
338
+ fragment.appendChild(minimapNodeElement);
339
+ });
340
+
341
+ minimapContentElement.innerHTML = '';
342
+ minimapContentElement.appendChild(fragment);
343
+ }
344
+
345
+ function getChartTransform(chartElement) {
346
+ let currentTransform;
347
+ let matrixValues;
348
+ let currentScale = 1;
349
+ let currentTranslateX = 0;
350
+ let currentTranslateY = 0;
351
+
352
+ if (!chartElement) {
353
+ return {
354
+ scale: currentScale,
355
+ translateX: currentTranslateX,
356
+ translateY: currentTranslateY
357
+ };
358
+ }
359
+
360
+ currentTransform = getWindow().getComputedStyle(chartElement).transform || chartElement.style.transform || 'none';
361
+ if (currentTransform && currentTransform !== 'none') {
362
+ if (currentTransform.indexOf('matrix3d(') === 0) {
363
+ matrixValues = currentTransform.slice(9, -1).split(',').map(function (value) {
364
+ return getWindow().parseFloat(value.trim());
365
+ });
366
+ currentScale = Math.abs(matrixValues[0]) || 1;
367
+ currentTranslateX = matrixValues[12] || 0;
368
+ currentTranslateY = matrixValues[13] || 0;
369
+ } else if (currentTransform.indexOf('matrix(') === 0) {
370
+ matrixValues = currentTransform.slice(7, -1).split(',').map(function (value) {
371
+ return getWindow().parseFloat(value.trim());
372
+ });
373
+ currentScale = Math.abs(matrixValues[0]) || 1;
374
+ currentTranslateX = matrixValues[4] || 0;
375
+ currentTranslateY = matrixValues[5] || 0;
376
+ } else if (currentTransform.indexOf('scale3d(') === 0) {
377
+ matrixValues = currentTransform.slice(8, -1).split(',').map(function (value) {
378
+ return getWindow().parseFloat(value.trim());
379
+ });
380
+ currentScale = Math.abs(matrixValues[0]) || 1;
381
+ } else if (currentTransform.indexOf('scale(') === 0) {
382
+ matrixValues = currentTransform.slice(6, -1).split(',').map(function (value) {
383
+ return getWindow().parseFloat(value.trim());
384
+ });
385
+ currentScale = Math.abs(matrixValues[0]) || 1;
386
+ }
387
+ }
388
+
389
+ return {
390
+ scale: currentScale,
391
+ translateX: currentTranslateX,
392
+ translateY: currentTranslateY
393
+ };
394
+ }
395
+
396
+ const OrgChart = function (elem, opts) {
397
+ if (!(this instanceof OrgChart)) {
398
+ return new OrgChart(elem, opts);
399
+ }
400
+ if (isPlainObject(elem) && !opts) {
401
+ this.opts = elem;
402
+ this.chartContainer = getElement(elem.chartContainer || elem.container);
403
+ } else {
404
+ this.chartContainer = getElement(elem);
405
+ this.opts = opts || {};
406
+ }
407
+ this.chart = null;
408
+ this.defaultOptions = {
409
+ 'icons': {
410
+ 'theme': 'oci',
411
+ 'parentNode': 'oci-menu',
412
+ 'expandToUp': 'oci-chevron-up',
413
+ 'collapseToDown': 'oci-chevron-down',
414
+ 'collapseToLeft': 'oci-chevron-left',
415
+ 'expandToRight': 'oci-chevron-right',
416
+ 'backToCompact': 'oci-corner-top-left',
417
+ 'backToLoose': 'oci-corner-bottom-right',
418
+ 'collapsed': 'oci-plus-square',
419
+ 'expanded': 'oci-minus-square',
420
+ 'spinner': 'oci-spinner'
421
+ },
422
+ 'nodeTitle': 'name',
423
+ 'nodeId': 'id',
424
+ 'toggleSiblingsResp': false,
425
+ 'visibleLevel': 999,
426
+ 'chartClass': '',
427
+ 'exportButton': false,
428
+ 'exportButtonName': 'Export',
429
+ 'exportFilename': 'OrgChart',
430
+ 'exportFileextension': 'png',
431
+ 'exportScale': 2,
432
+ 'draggable': false,
433
+ 'direction': 't2b',
434
+ 'pan': false,
435
+ 'minimap': false,
436
+ 'zoom': false,
437
+ 'zoominLimit': 7,
438
+ 'zoomoutLimit': 0.5
439
+ };
440
+ if (this.chartContainer) {
441
+ this.init();
442
+ }
443
+ };
444
+
445
+ if (typeof module === 'object' && typeof module.exports === 'object') {
446
+ Object.defineProperty(OrgChart, '__testing__', {
447
+ value: {
448
+ getState: getState,
449
+ setState: setState
450
+ }
451
+ });
452
+ }
453
+ //
454
+ OrgChart.prototype = {
455
+ //
456
+ init: function (opts) {
457
+ this.options = mergeObjects({}, this.defaultOptions, this.opts, opts);
458
+ // build the org-chart
459
+ const chartContainerElement = this.chartContainer;
460
+ let chartElement;
461
+ let rootElement;
462
+ let rootNodesContainerElement;
463
+ let rootHierarchyElement;
464
+
465
+ this.detachMinimap();
466
+ if (this.chart) {
467
+ this.chart.remove();
468
+ }
469
+ const data = this.options.data;
470
+ chartElement = getDocument().createElement('div');
471
+ chartElement.className = 'orgchart' + (this.options.chartClass !== '' ? ' ' + this.options.chartClass : '') + (this.options.direction !== 't2b' ? ' ' + this.options.direction : '');
472
+ setState(chartElement, 'options', this.options);
473
+ this.chart = chartElement;
474
+ chartElement.addEventListener('click', function (event) {
475
+ const nativeEvent = event.originalEvent || event;
476
+ const clickTargetElement = getElement(nativeEvent.target);
477
+
478
+ if (!clickTargetElement || !(typeof clickTargetElement.closest === 'function' && clickTargetElement.closest('.node'))) {
479
+ Array.from(chartElement.querySelectorAll('.node.focused')).forEach(function (focusedNodeEl) {
480
+ focusedNodeEl.classList.remove('focused');
481
+ });
482
+ }
483
+ });
484
+ if (typeof getWindow().MutationObserver !== 'undefined') {
485
+ this.triggerInitEvent();
486
+ }
487
+ const isListElementData = isElement(data) && data.tagName === 'UL';
488
+ rootNodesContainerElement = getDocument().createElement('ul');
489
+ rootNodesContainerElement.className = 'nodes';
490
+ chartElement.appendChild(rootNodesContainerElement);
491
+
492
+ if (!isListElementData && Array.isArray(data)) {
493
+ rootElement = rootNodesContainerElement;
494
+ } else {
495
+ rootHierarchyElement = getDocument().createElement('li');
496
+ rootHierarchyElement.className = 'hierarchy';
497
+ rootNodesContainerElement.appendChild(rootHierarchyElement);
498
+ rootElement = rootHierarchyElement;
499
+ }
500
+
501
+ if (isListElementData) { // ul datasource
502
+ this.buildHierarchy(rootElement, this.buildJsonDS(data.firstElementChild));
503
+ } else { // local json datasource
504
+ if (data.relationship) {
505
+ this.buildHierarchy(rootElement, data);
506
+ } else {
507
+ this.buildHierarchy(rootElement, Array.isArray(data) ? data : this.attachRel(data, '00'));
508
+ }
509
+ }
510
+
511
+ if (chartContainerElement) {
512
+ chartContainerElement.appendChild(chartElement);
513
+ }
514
+
515
+ // append the export button
516
+ if (this.options.exportButton && !getDocument().querySelector('.oc-export-btn')) {
517
+ this.attachExportButton();
518
+ }
519
+
520
+ if (this.options.pan) {
521
+ this.bindPan();
522
+ }
523
+
524
+ if (this.options.zoom) {
525
+ this.bindZoom();
526
+ }
527
+
528
+ if (this.options.minimap) {
529
+ this.attachMinimap();
530
+ }
531
+
532
+ return this;
533
+ },
534
+ handleCompactNodes: function () {
535
+ const chartElement = this.chart;
536
+
537
+ if (!chartElement) {
538
+ return;
539
+ }
540
+ // caculate the compact nodes' level which is used to add different styles
541
+ Array.from(chartElement.querySelectorAll('.node.compact')).forEach(function (compactNodeEl) {
542
+ let compactAncestorCount = 0;
543
+ let currentAncestorElement = compactNodeEl.parentElement;
544
+
545
+ while (currentAncestorElement) {
546
+ if (currentAncestorElement.classList && currentAncestorElement.classList.contains('compact')) {
547
+ compactAncestorCount += 1;
548
+ }
549
+ currentAncestorElement = currentAncestorElement.parentElement;
550
+ }
551
+
552
+ compactNodeEl.classList.add(compactAncestorCount % 2 === 0 ? 'even' : 'odd');
553
+ }); // the following code snippets are kept only as a reminder for a future native-DOM approach
554
+ // that would add direction arrows for the top compact node once the styles are corrected.
555
+ },
556
+ //
557
+ triggerInitEvent: function () {
558
+ const orgChart = this;
559
+ const MutationObserverCtor = getWindow().MutationObserver;
560
+ const mo = new MutationObserverCtor(function (mutations) {
561
+ mo.disconnect();
562
+ initTime:
563
+ for (let i = 0; i < mutations.length; i++) {
564
+ for (let j = 0; j < mutations[i].addedNodes.length; j++) {
565
+ if (mutations[i].addedNodes[j].classList && mutations[i].addedNodes[j].classList.contains('orgchart')) {
566
+ orgChart.handleCompactNodes();
567
+ if (!orgChart.chart) {
568
+ break initTime;
569
+ }
570
+ if (orgChart.options.initCompleted && typeof orgChart.options.initCompleted === 'function') {
571
+ orgChart.options.initCompleted(orgChart.chart);
572
+ }
573
+ mutations[i].addedNodes[j].dispatchEvent(createTriggeredEvent('init.orgchart'));
574
+ break initTime;
575
+ }
576
+ }
577
+ }
578
+ });
579
+ mo.observe(this.chartContainer, { childList: true });
580
+ },
581
+ triggerShowEvent: function (targetEl, rel) {
582
+ if (targetEl && typeof targetEl.dispatchEvent === 'function') {
583
+ targetEl.dispatchEvent(createTriggeredEvent('show-' + rel + '.orgchart'));
584
+ }
585
+ },
586
+ triggerHideEvent: function (targetEl, rel) {
587
+ if (targetEl && typeof targetEl.dispatchEvent === 'function') {
588
+ targetEl.dispatchEvent(createTriggeredEvent('hide-' + rel + '.orgchart'));
589
+ }
590
+ },
591
+ // add export button for orgchart
592
+ attachExportButton: function () {
593
+ const orgChart = this;
594
+ const chartContainerElement = this.chartContainer;
595
+ let exportButtonElement;
596
+
597
+ if (!chartContainerElement || !chartContainerElement.parentElement) {
598
+ return;
599
+ }
600
+
601
+ exportButtonElement = getDocument().createElement('button');
602
+ exportButtonElement.className = 'oc-export-btn';
603
+ exportButtonElement.textContent = this.options.exportButtonName;
604
+ exportButtonElement.addEventListener('click', function (event) {
605
+ event.preventDefault();
606
+ orgChart.export();
607
+ });
608
+ chartContainerElement.insertAdjacentElement('afterend', exportButtonElement);
609
+ },
610
+ setOptions: function (opts, val) {
611
+ if (typeof opts === 'string') {
612
+ if (opts === 'pan') {
613
+ if (val) {
614
+ this.bindPan();
615
+ } else {
616
+ this.unbindPan();
617
+ }
618
+ }
619
+ if (opts === 'zoom') {
620
+ if (val) {
621
+ this.bindZoom();
622
+ } else {
623
+ this.unbindZoom();
624
+ }
625
+ }
626
+ if (opts === 'minimap') {
627
+ this.options = mergeObjects({}, this.options || {}, { minimap: val });
628
+ if (this.chart) {
629
+ setState(this.chart, 'options', this.options);
630
+ }
631
+ if (val) {
632
+ this.attachMinimap();
633
+ } else {
634
+ this.detachMinimap();
635
+ }
636
+ }
637
+ }
638
+ if (typeof opts === 'object') {
639
+ if (opts.data) {
640
+ this.init(opts);
641
+ } else {
642
+ if (typeof opts.pan !== 'undefined') {
643
+ if (opts.pan) {
644
+ this.bindPan();
645
+ } else {
646
+ this.unbindPan();
647
+ }
648
+ }
649
+ if (typeof opts.zoom !== 'undefined') {
650
+ if (opts.zoom) {
651
+ this.bindZoom();
652
+ } else {
653
+ this.unbindZoom();
654
+ }
655
+ }
656
+ if (typeof opts.minimap !== 'undefined') {
657
+ this.options = mergeObjects({}, this.options || {}, { minimap: opts.minimap });
658
+ if (this.chart) {
659
+ setState(this.chart, 'options', this.options);
660
+ }
661
+ if (opts.minimap) {
662
+ this.attachMinimap();
663
+ } else {
664
+ this.detachMinimap();
665
+ }
666
+ }
667
+ }
668
+ }
669
+
670
+ return this;
671
+ },
672
+ attachMinimap: function () {
673
+ const orgChart = this;
674
+ const chartContainerElement = this.chartContainer;
675
+ const chartElement = this.chart;
676
+ let minimapElement;
677
+ let minimapStageElement;
678
+ let minimapContentElement;
679
+ let minimapViewportElement;
680
+ let minimapWheelBoundHandler;
681
+ let minimapPointerDownBoundHandler;
682
+ let minimapMutationObserver;
683
+
684
+ if (!chartContainerElement || !chartElement) {
685
+ return;
686
+ }
687
+
688
+ this.detachMinimap();
689
+ if (!getWindow().getComputedStyle(chartContainerElement).position || getWindow().getComputedStyle(chartContainerElement).position === 'static') {
690
+ setState(chartContainerElement, 'minimapOriginalPosition', chartContainerElement.style.position || '');
691
+ setState(chartContainerElement, 'minimapManagedPosition', true);
692
+ chartContainerElement.style.position = 'relative';
693
+ }
694
+
695
+ minimapElement = getDocument().createElement('div');
696
+ minimapElement.className = 'orgchart-minimap';
697
+ minimapStageElement = getDocument().createElement('div');
698
+ minimapStageElement.className = 'orgchart-minimap-stage';
699
+ minimapContentElement = getDocument().createElement('div');
700
+ minimapContentElement.className = 'orgchart-minimap-content';
701
+ minimapViewportElement = getDocument().createElement('div');
702
+ minimapViewportElement.className = 'orgchart-minimap-viewport';
703
+
704
+ minimapStageElement.appendChild(minimapContentElement);
705
+ minimapStageElement.appendChild(minimapViewportElement);
706
+ minimapElement.appendChild(minimapStageElement);
707
+ chartContainerElement.appendChild(minimapElement);
708
+
709
+ minimapWheelBoundHandler = function (event) {
710
+ orgChart.minimapWheelHandler(event);
711
+ };
712
+ minimapPointerDownBoundHandler = function (event) {
713
+ orgChart.minimapStartHandler(event);
714
+ };
715
+
716
+ minimapElement.addEventListener('wheel', minimapWheelBoundHandler);
717
+ minimapViewportElement.addEventListener('mousedown', minimapPointerDownBoundHandler);
718
+
719
+ if (typeof getWindow().MutationObserver !== 'undefined') {
720
+ minimapMutationObserver = new (getWindow().MutationObserver)(function () {
721
+ orgChart.updateMinimap(true);
722
+ });
723
+ minimapMutationObserver.observe(chartElement, {
724
+ childList: true,
725
+ subtree: true,
726
+ attributes: true,
727
+ attributeFilter: ['class']
728
+ });
729
+ }
730
+
731
+ setState(chartElement, 'minimapElement', minimapElement);
732
+ setState(chartElement, 'minimapStageElement', minimapStageElement);
733
+ setState(chartElement, 'minimapContentElement', minimapContentElement);
734
+ setState(chartElement, 'minimapViewportElement', minimapViewportElement);
735
+ setState(chartElement, 'minimapWheelBoundHandler', minimapWheelBoundHandler);
736
+ setState(chartElement, 'minimapPointerDownBoundHandler', minimapPointerDownBoundHandler);
737
+ setState(chartElement, 'minimapMutationObserver', minimapMutationObserver || null);
738
+
739
+ this.updateMinimap(true);
740
+ },
741
+ detachMinimap: function () {
742
+ const chartContainerElement = this.chartContainer;
743
+ const chartElement = this.chart;
744
+ let minimapElement;
745
+ let minimapViewportElement;
746
+ let minimapWheelBoundHandler;
747
+ let minimapPointerDownBoundHandler;
748
+ let minimapMoveBoundHandler;
749
+ let minimapEndBoundHandler;
750
+ let minimapMutationObserver;
751
+
752
+ if (chartElement) {
753
+ minimapElement = getState(chartElement, 'minimapElement');
754
+ minimapViewportElement = getState(chartElement, 'minimapViewportElement');
755
+ minimapWheelBoundHandler = getState(chartElement, 'minimapWheelBoundHandler');
756
+ minimapPointerDownBoundHandler = getState(chartElement, 'minimapPointerDownBoundHandler');
757
+ minimapMoveBoundHandler = getState(chartElement, 'minimapMoveBoundHandler');
758
+ minimapEndBoundHandler = getState(chartElement, 'minimapEndBoundHandler');
759
+ minimapMutationObserver = getState(chartElement, 'minimapMutationObserver');
760
+
761
+ if (minimapElement && minimapWheelBoundHandler) {
762
+ minimapElement.removeEventListener('wheel', minimapWheelBoundHandler);
763
+ }
764
+ if (minimapViewportElement && minimapPointerDownBoundHandler) {
765
+ minimapViewportElement.removeEventListener('mousedown', minimapPointerDownBoundHandler);
766
+ }
767
+ if (minimapMoveBoundHandler) {
768
+ getDocument().removeEventListener('mousemove', minimapMoveBoundHandler);
769
+ }
770
+ if (minimapEndBoundHandler) {
771
+ getDocument().removeEventListener('mouseup', minimapEndBoundHandler);
772
+ }
773
+ if (minimapMutationObserver) {
774
+ minimapMutationObserver.disconnect();
775
+ }
776
+ if (minimapElement && minimapElement.parentElement) {
777
+ minimapElement.remove();
778
+ }
779
+
780
+ [
781
+ 'minimapElement',
782
+ 'minimapStageElement',
783
+ 'minimapContentElement',
784
+ 'minimapViewportElement',
785
+ 'minimapWheelBoundHandler',
786
+ 'minimapPointerDownBoundHandler',
787
+ 'minimapMoveBoundHandler',
788
+ 'minimapEndBoundHandler',
789
+ 'minimapMutationObserver',
790
+ 'minimapDragState',
791
+ 'minimapScale',
792
+ 'minimapOffsetX',
793
+ 'minimapOffsetY',
794
+ 'minimapContentWidth',
795
+ 'minimapContentHeight'
796
+ ].forEach(function (key) {
797
+ setState(chartElement, key, null);
798
+ });
799
+ }
800
+
801
+ if (chartContainerElement && getState(chartContainerElement, 'minimapManagedPosition')) {
802
+ chartContainerElement.style.position = getState(chartContainerElement, 'minimapOriginalPosition') || '';
803
+ setState(chartContainerElement, 'minimapManagedPosition', null);
804
+ setState(chartContainerElement, 'minimapOriginalPosition', null);
805
+ }
806
+ },
807
+ minimapWheelHandler: function (event) {
808
+ const chartContainerElement = this.chartContainer;
809
+
810
+ if (!this.chart || !chartContainerElement) {
811
+ return;
812
+ }
813
+ if (typeof event.preventDefault === 'function') {
814
+ event.preventDefault();
815
+ }
816
+
817
+ this.setChartScale(this.chart, 1 + (event.deltaY > 0 ? -0.2 : 0.2), {
818
+ x: chartContainerElement.getBoundingClientRect().left + (chartContainerElement.clientWidth / 2),
819
+ y: chartContainerElement.getBoundingClientRect().top + (chartContainerElement.clientHeight / 2)
820
+ });
821
+ },
822
+ minimapStartHandler: function (event) {
823
+ const chartElement = this.chart;
824
+ const minimapViewportElement = chartElement ? getState(chartElement, 'minimapViewportElement') : null;
825
+ const minimapMoveBoundHandler = this.minimapMoveHandler.bind(this);
826
+ const minimapEndBoundHandler = this.minimapEndHandler.bind(this);
827
+ const viewportRect = minimapViewportElement ? minimapViewportElement.getBoundingClientRect() : null;
828
+
829
+ if (!chartElement || !minimapViewportElement || !viewportRect) {
830
+ return;
831
+ }
832
+ if (typeof event.preventDefault === 'function') {
833
+ event.preventDefault();
834
+ }
835
+
836
+ setState(chartElement, 'minimapDragState', {
837
+ startClientX: event.clientX,
838
+ startClientY: event.clientY,
839
+ startLeft: viewportRect.left,
840
+ startTop: viewportRect.top,
841
+ viewportWidth: viewportRect.width,
842
+ viewportHeight: viewportRect.height
843
+ });
844
+ setState(chartElement, 'minimapMoveBoundHandler', minimapMoveBoundHandler);
845
+ setState(chartElement, 'minimapEndBoundHandler', minimapEndBoundHandler);
846
+ getDocument().addEventListener('mousemove', minimapMoveBoundHandler);
847
+ getDocument().addEventListener('mouseup', minimapEndBoundHandler);
848
+ },
849
+ minimapMoveHandler: function (event) {
850
+ const chartElement = this.chart;
851
+ const dragState = chartElement ? getState(chartElement, 'minimapDragState') : null;
852
+ const minimapStageElement = chartElement ? getState(chartElement, 'minimapStageElement') : null;
853
+ let stageRect;
854
+
855
+ if (!chartElement || !dragState || !minimapStageElement) {
856
+ return;
857
+ }
858
+
859
+ stageRect = minimapStageElement.getBoundingClientRect();
860
+ this.moveChartFromMinimap(
861
+ dragState.startLeft + (event.clientX - dragState.startClientX) - stageRect.left,
862
+ dragState.startTop + (event.clientY - dragState.startClientY) - stageRect.top,
863
+ dragState.viewportWidth,
864
+ dragState.viewportHeight
865
+ );
866
+ },
867
+ minimapEndHandler: function () {
868
+ const chartElement = this.chart;
869
+ const minimapMoveBoundHandler = chartElement ? getState(chartElement, 'minimapMoveBoundHandler') : null;
870
+ const minimapEndBoundHandler = chartElement ? getState(chartElement, 'minimapEndBoundHandler') : null;
871
+
872
+ if (minimapMoveBoundHandler) {
873
+ getDocument().removeEventListener('mousemove', minimapMoveBoundHandler);
874
+ setState(chartElement, 'minimapMoveBoundHandler', null);
875
+ }
876
+ if (minimapEndBoundHandler) {
877
+ getDocument().removeEventListener('mouseup', minimapEndBoundHandler);
878
+ setState(chartElement, 'minimapEndBoundHandler', null);
879
+ }
880
+ if (chartElement) {
881
+ setState(chartElement, 'minimapDragState', null);
882
+ }
883
+ },
884
+ moveChartFromMinimap: function (stageLeft, stageTop, viewportWidth, viewportHeight) {
885
+ const chartElement = this.chart;
886
+ const chartContainerElement = this.chartContainer;
887
+ const minimapStageElement = chartElement ? getState(chartElement, 'minimapStageElement') : null;
888
+ const minimapScale = chartElement ? getState(chartElement, 'minimapScale') : null;
889
+ const minimapOffsetX = chartElement ? getState(chartElement, 'minimapOffsetX') : 0;
890
+ const minimapOffsetY = chartElement ? getState(chartElement, 'minimapOffsetY') : 0;
891
+ const minimapContentWidth = chartElement ? getState(chartElement, 'minimapContentWidth') : 0;
892
+ const minimapContentHeight = chartElement ? getState(chartElement, 'minimapContentHeight') : 0;
893
+ const chartTransform = getChartTransform(chartElement);
894
+ const chartRect = chartElement ? chartElement.getBoundingClientRect() : null;
895
+ const containerRect = chartContainerElement ? chartContainerElement.getBoundingClientRect() : null;
896
+ let localViewportWidth;
897
+ let localViewportHeight;
898
+ let localLeft;
899
+ let localTop;
900
+ let baseLeft;
901
+ let baseTop;
902
+
903
+ if (!chartElement || !chartContainerElement || !minimapStageElement || !minimapScale || !chartRect || !containerRect) {
904
+ return;
905
+ }
906
+
907
+ localViewportWidth = viewportWidth / minimapScale;
908
+ localViewportHeight = viewportHeight / minimapScale;
909
+ localLeft = clampValue((stageLeft - minimapOffsetX) / minimapScale, 0, Math.max(minimapContentWidth - localViewportWidth, 0));
910
+ localTop = clampValue((stageTop - minimapOffsetY) / minimapScale, 0, Math.max(minimapContentHeight - localViewportHeight, 0));
911
+ baseLeft = chartRect.left - chartTransform.translateX;
912
+ baseTop = chartRect.top - chartTransform.translateY;
913
+
914
+ chartElement.style.transformOrigin = '0 0';
915
+ chartElement.style.transform = 'matrix(' + chartTransform.scale + ', 0, 0, ' + chartTransform.scale + ', '
916
+ + (containerRect.left - baseLeft - (localLeft * chartTransform.scale)) + ', '
917
+ + (containerRect.top - baseTop - (localTop * chartTransform.scale)) + ')';
918
+ this.updateMinimap();
919
+ },
920
+ updateMinimap: function (forceRefresh) {
921
+ const chartElement = this.chart;
922
+ const chartContainerElement = this.chartContainer;
923
+ const minimapStageElement = chartElement ? getState(chartElement, 'minimapStageElement') : null;
924
+ const minimapContentElement = chartElement ? getState(chartElement, 'minimapContentElement') : null;
925
+ const minimapViewportElement = chartElement ? getState(chartElement, 'minimapViewportElement') : null;
926
+ const chartTransform = getChartTransform(chartElement);
927
+ const chartRect = chartElement ? chartElement.getBoundingClientRect() : null;
928
+ const containerRect = chartContainerElement ? chartContainerElement.getBoundingClientRect() : null;
929
+ let contentWidth;
930
+ let contentHeight;
931
+ let stageWidth;
932
+ let stageHeight;
933
+ let minimapScale;
934
+ let minimapOffsetX;
935
+ let minimapOffsetY;
936
+ let viewportLeft;
937
+ let viewportTop;
938
+ let viewportWidth;
939
+ let viewportHeight;
940
+
941
+ if (!this.options || !this.options.minimap || !chartElement || !chartContainerElement || !minimapStageElement || !minimapContentElement || !minimapViewportElement || !chartRect || !containerRect) {
942
+ return;
943
+ }
944
+
945
+ contentWidth = chartTransform.scale !== 0 && chartRect.width
946
+ ? chartRect.width / chartTransform.scale
947
+ : (chartElement.scrollWidth || chartElement.offsetWidth || 1);
948
+ contentHeight = chartTransform.scale !== 0 && chartRect.height
949
+ ? chartRect.height / chartTransform.scale
950
+ : (chartElement.scrollHeight || chartElement.offsetHeight || 1);
951
+ stageWidth = minimapStageElement.clientWidth || 180;
952
+ stageHeight = minimapStageElement.clientHeight || 120;
953
+ minimapScale = Math.min(stageWidth / Math.max(contentWidth, 1), stageHeight / Math.max(contentHeight, 1));
954
+ minimapOffsetX = (stageWidth - (contentWidth * minimapScale)) / 2;
955
+ minimapOffsetY = (stageHeight - (contentHeight * minimapScale)) / 2;
956
+
957
+ minimapContentElement.style.width = contentWidth + 'px';
958
+ minimapContentElement.style.height = contentHeight + 'px';
959
+ minimapContentElement.style.transformOrigin = '0 0';
960
+ minimapContentElement.style.transform = 'translate(' + minimapOffsetX + 'px, ' + minimapOffsetY + 'px) scale(' + minimapScale + ')';
961
+
962
+ if (forceRefresh || !minimapContentElement.firstElementChild) {
963
+ renderMinimapNodes(chartElement, minimapContentElement, chartRect, chartTransform.scale || 1);
964
+ }
965
+
966
+ viewportLeft = clampValue((containerRect.left - chartRect.left) / chartTransform.scale, 0, Math.max(contentWidth, 0));
967
+ viewportTop = clampValue((containerRect.top - chartRect.top) / chartTransform.scale, 0, Math.max(contentHeight, 0));
968
+ viewportWidth = Math.min(contentWidth, containerRect.width / chartTransform.scale);
969
+ viewportHeight = Math.min(contentHeight, containerRect.height / chartTransform.scale);
970
+
971
+ minimapViewportElement.style.width = Math.max(viewportWidth * minimapScale, 12) + 'px';
972
+ minimapViewportElement.style.height = Math.max(viewportHeight * minimapScale, 12) + 'px';
973
+ minimapViewportElement.style.transform = 'translate(' + (minimapOffsetX + (viewportLeft * minimapScale)) + 'px, ' + (minimapOffsetY + (viewportTop * minimapScale)) + 'px)';
974
+
975
+ setState(chartElement, 'minimapScale', minimapScale);
976
+ setState(chartElement, 'minimapOffsetX', minimapOffsetX);
977
+ setState(chartElement, 'minimapOffsetY', minimapOffsetY);
978
+ setState(chartElement, 'minimapContentWidth', contentWidth);
979
+ setState(chartElement, 'minimapContentHeight', contentHeight);
980
+ },
981
+ //
982
+ panStartHandler: function (e) {
983
+ const orgChart = this;
984
+ const nativeEvent = e.originalEvent || e;
985
+ const chartElement = getElement(e.chart || nativeEvent.currentTarget);
986
+ const targetElement = getElement(nativeEvent.target);
987
+ let lastTf;
988
+ let existingPanMoveHandler;
989
+ let panMoveHandler;
990
+
991
+ if (!chartElement) {
992
+ return;
993
+ }
994
+
995
+ if ((targetElement && typeof targetElement.closest === 'function' && targetElement.closest('.node')) || (nativeEvent.touches && nativeEvent.touches.length > 1)) {
996
+ setState(chartElement, 'panning', false);
997
+ return;
998
+ } else {
999
+ chartElement.style.cursor = 'move';
1000
+ setState(chartElement, 'panning', true);
1001
+ }
1002
+ let lastX = 0;
1003
+ let lastY = 0;
1004
+ lastTf = chartElement.style.transform || getWindow().getComputedStyle(chartElement).transform || 'none';
1005
+ if (lastTf !== 'none') {
1006
+ const temp = lastTf.split(',');
1007
+ if (lastTf.indexOf('3d') === -1) {
1008
+ lastX = parseInt(temp[4]);
1009
+ lastY = parseInt(temp[5]);
1010
+ } else {
1011
+ lastX = parseInt(temp[12]);
1012
+ lastY = parseInt(temp[13]);
1013
+ }
1014
+ }
1015
+ let startX = 0;
1016
+ let startY = 0;
1017
+ if (!nativeEvent.targetTouches) { // pand on desktop
1018
+ startX = (typeof nativeEvent.pageX === 'number' ? nativeEvent.pageX : nativeEvent.clientX) - lastX;
1019
+ startY = (typeof nativeEvent.pageY === 'number' ? nativeEvent.pageY : nativeEvent.clientY) - lastY;
1020
+ } else if (nativeEvent.targetTouches.length === 1) { // pan on mobile device
1021
+ startX = nativeEvent.targetTouches[0].pageX - lastX;
1022
+ startY = nativeEvent.targetTouches[0].pageY - lastY;
1023
+ } else if (nativeEvent.targetTouches.length > 1) {
1024
+ return;
1025
+ }
1026
+
1027
+ existingPanMoveHandler = getState(chartElement, 'panMoveHandler');
1028
+ if (existingPanMoveHandler) {
1029
+ chartElement.removeEventListener('mousemove', existingPanMoveHandler);
1030
+ chartElement.removeEventListener('touchmove', existingPanMoveHandler);
1031
+ }
1032
+
1033
+ panMoveHandler = function (e) {
1034
+ let currentTransform;
1035
+ let matrix;
1036
+
1037
+ if (!getState(chartElement, 'panning')) {
1038
+ return;
1039
+ }
1040
+ let newX = 0;
1041
+ let newY = 0;
1042
+ if (!e.targetTouches) { // pand on desktop
1043
+ newX = (typeof e.pageX === 'number' ? e.pageX : e.clientX) - startX;
1044
+ newY = (typeof e.pageY === 'number' ? e.pageY : e.clientY) - startY;
1045
+ } else if (e.targetTouches.length === 1) { // pan on mobile device
1046
+ newX = e.targetTouches[0].pageX - startX;
1047
+ newY = e.targetTouches[0].pageY - startY;
1048
+ } else if (e.targetTouches.length > 1) {
1049
+ return;
1050
+ }
1051
+ currentTransform = chartElement.style.transform || getWindow().getComputedStyle(chartElement).transform || 'none';
1052
+ if (currentTransform === 'none') {
1053
+ if (currentTransform.indexOf('3d') === -1) {
1054
+ chartElement.style.transform = 'matrix(1, 0, 0, 1, ' + newX + ', ' + newY + ')';
1055
+ } else {
1056
+ chartElement.style.transform = 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + newX + ', ' + newY + ', 0, 1)';
1057
+ }
1058
+ } else {
1059
+ matrix = currentTransform.split(',');
1060
+ if (currentTransform.indexOf('3d') === -1) {
1061
+ matrix[4] = ' ' + newX;
1062
+ matrix[5] = ' ' + newY + ')';
1063
+ } else {
1064
+ matrix[12] = ' ' + newX;
1065
+ matrix[13] = ' ' + newY;
1066
+ }
1067
+ chartElement.style.transform = matrix.join(',');
1068
+ orgChart.updateMinimap();
1069
+ }
1070
+ };
1071
+
1072
+ setState(chartElement, 'panMoveHandler', panMoveHandler);
1073
+ chartElement.addEventListener('mousemove', panMoveHandler);
1074
+ chartElement.addEventListener('touchmove', panMoveHandler);
1075
+ },
1076
+ //
1077
+ panEndHandler: function (e) {
1078
+ const nativeEvent = e.originalEvent || e;
1079
+ const chartElement = getElement(e.chart || nativeEvent.chart || nativeEvent.currentTarget);
1080
+ let panMoveHandler;
1081
+
1082
+ if (!chartElement) {
1083
+ return;
1084
+ }
1085
+
1086
+ if (getState(chartElement, 'panning')) {
1087
+ panMoveHandler = getState(chartElement, 'panMoveHandler');
1088
+ setState(chartElement, 'panning', false);
1089
+ chartElement.style.cursor = 'default';
1090
+ if (panMoveHandler) {
1091
+ chartElement.removeEventListener('mousemove', panMoveHandler);
1092
+ chartElement.removeEventListener('touchmove', panMoveHandler);
1093
+ setState(chartElement, 'panMoveHandler', null);
1094
+ }
1095
+ }
1096
+ },
1097
+ //
1098
+ bindPan: function () {
1099
+ const orgChart = this;
1100
+ const chartContainerElement = this.chartContainer;
1101
+ const chartElement = this.chart;
1102
+ let panStartBoundHandler;
1103
+ let panEndBoundHandler;
1104
+
1105
+ if (!chartContainerElement || !chartElement) {
1106
+ return;
1107
+ }
1108
+
1109
+ this.unbindPan();
1110
+ chartContainerElement.style.overflow = 'hidden';
1111
+ panStartBoundHandler = function (event) {
1112
+ orgChart.panStartHandler({
1113
+ chart: chartElement,
1114
+ originalEvent: event
1115
+ });
1116
+ };
1117
+ panEndBoundHandler = function () {
1118
+ orgChart.panEndHandler({
1119
+ chart: chartElement
1120
+ });
1121
+ };
1122
+
1123
+ setState(chartElement, 'panStartBoundHandler', panStartBoundHandler);
1124
+ setState(chartElement, 'panEndBoundHandler', panEndBoundHandler);
1125
+ chartElement.addEventListener('mousedown', panStartBoundHandler);
1126
+ chartElement.addEventListener('touchstart', panStartBoundHandler);
1127
+ getDocument().addEventListener('mouseup', panEndBoundHandler);
1128
+ getDocument().addEventListener('touchend', panEndBoundHandler);
1129
+ },
1130
+ //
1131
+ unbindPan: function () {
1132
+ const chartContainerElement = this.chartContainer;
1133
+ const chartElement = this.chart;
1134
+ let panStartBoundHandler;
1135
+ let panEndBoundHandler;
1136
+
1137
+ if (!chartContainerElement || !chartElement) {
1138
+ return;
1139
+ }
1140
+
1141
+ chartContainerElement.style.overflow = 'auto';
1142
+ panStartBoundHandler = getState(chartElement, 'panStartBoundHandler');
1143
+ panEndBoundHandler = getState(chartElement, 'panEndBoundHandler');
1144
+ if (panStartBoundHandler) {
1145
+ chartElement.removeEventListener('mousedown', panStartBoundHandler);
1146
+ chartElement.removeEventListener('touchstart', panStartBoundHandler);
1147
+ setState(chartElement, 'panStartBoundHandler', null);
1148
+ }
1149
+ if (panEndBoundHandler) {
1150
+ getDocument().removeEventListener('mouseup', panEndBoundHandler);
1151
+ getDocument().removeEventListener('touchend', panEndBoundHandler);
1152
+ setState(chartElement, 'panEndBoundHandler', null);
1153
+ }
1154
+ },
1155
+ //
1156
+ zoomWheelHandler: function (e) {
1157
+ const nativeEvent = e.originalEvent || e;
1158
+ const orgChart = e.orgChart;
1159
+ const chartContainerElement = orgChart ? orgChart.chartContainer : null;
1160
+ const chartContainerRect = chartContainerElement && typeof chartContainerElement.getBoundingClientRect === 'function'
1161
+ ? chartContainerElement.getBoundingClientRect()
1162
+ : null;
1163
+ const hasPointerPosition = nativeEvent && typeof nativeEvent.clientX === 'number' && typeof nativeEvent.clientY === 'number';
1164
+ let zoomAnchor = null;
1165
+
1166
+ if (!orgChart || !orgChart.chart || !chartContainerElement) {
1167
+ return;
1168
+ }
1169
+
1170
+ if (chartContainerRect && hasPointerPosition) {
1171
+ if (nativeEvent.clientX < chartContainerRect.left || nativeEvent.clientX > chartContainerRect.right
1172
+ || nativeEvent.clientY < chartContainerRect.top || nativeEvent.clientY > chartContainerRect.bottom) {
1173
+ return;
1174
+ }
1175
+
1176
+ zoomAnchor = {
1177
+ x: nativeEvent.clientX,
1178
+ y: nativeEvent.clientY
1179
+ };
1180
+ }
1181
+
1182
+ if (nativeEvent && typeof nativeEvent.preventDefault === 'function') {
1183
+ nativeEvent.preventDefault();
1184
+ }
1185
+ const newScale = 1 + (nativeEvent.deltaY > 0 ? -0.2 : 0.2);
1186
+ orgChart.setChartScale(orgChart.chart, newScale, zoomAnchor);
1187
+ },
1188
+ //
1189
+ zoomStartHandler: function (e) {
1190
+ let orgChart;
1191
+ let chartElement;
1192
+ let dist;
1193
+ let pinchCenter;
1194
+ const nativeEvent = e.originalEvent || e;
1195
+
1196
+ if(nativeEvent.touches && nativeEvent.touches.length === 2) {
1197
+ orgChart = e.orgChart;
1198
+ chartElement = orgChart.chart;
1199
+ if (!chartElement) {
1200
+ return;
1201
+ }
1202
+ setState(chartElement, 'pinching', true);
1203
+ dist = orgChart.getPinchDist(nativeEvent);
1204
+ pinchCenter = orgChart.getPinchCenter(nativeEvent);
1205
+ setState(chartElement, 'pinchDistStart', dist);
1206
+ setState(chartElement, 'pinchCenterStart', pinchCenter);
1207
+ setState(chartElement, 'pinchCenterEnd', pinchCenter);
1208
+ }
1209
+ },
1210
+ zoomingHandler: function (e) {
1211
+ const nativeEvent = e.originalEvent || e;
1212
+ const orgChart = e.orgChart;
1213
+ const chartElement = orgChart.chart;
1214
+ let dist;
1215
+ let pinchCenter;
1216
+
1217
+ if(chartElement && getState(chartElement, 'pinching')) {
1218
+ dist = orgChart.getPinchDist(nativeEvent);
1219
+ pinchCenter = orgChart.getPinchCenter(nativeEvent);
1220
+ setState(chartElement, 'pinchDistEnd', dist);
1221
+ setState(chartElement, 'pinchCenterEnd', pinchCenter);
1222
+ }
1223
+ },
1224
+ zoomEndHandler: function (e) {
1225
+ const orgChart = e.orgChart;
1226
+ const chartElement = orgChart.chart;
1227
+ let diff;
1228
+ let pinchCenter;
1229
+
1230
+ if(chartElement && getState(chartElement, 'pinching')) {
1231
+ setState(chartElement, 'pinching', false);
1232
+ diff = getState(chartElement, 'pinchDistEnd') - getState(chartElement, 'pinchDistStart');
1233
+ pinchCenter = getState(chartElement, 'pinchCenterEnd') || getState(chartElement, 'pinchCenterStart') || null;
1234
+ if (diff > 0) {
1235
+ orgChart.setChartScale(orgChart.chart, 1.2, pinchCenter);
1236
+ } else if (diff < 0) {
1237
+ orgChart.setChartScale(orgChart.chart, 0.8, pinchCenter);
1238
+ }
1239
+ }
1240
+ },
1241
+ //
1242
+ bindZoom: function () {
1243
+ const orgChart = this;
1244
+ const chartContainerElement = this.chartContainer;
1245
+ const chartElement = this.chart;
1246
+ let zoomWheelBoundHandler;
1247
+ let zoomStartBoundHandler;
1248
+ let zoomingBoundHandler;
1249
+ let zoomEndBoundHandler;
1250
+
1251
+ if (!chartContainerElement || !chartElement) {
1252
+ return;
1253
+ }
1254
+
1255
+ this.unbindZoom();
1256
+ zoomWheelBoundHandler = function (event) {
1257
+ orgChart.zoomWheelHandler({
1258
+ orgChart: orgChart,
1259
+ originalEvent: event
1260
+ });
1261
+ };
1262
+ zoomStartBoundHandler = function (event) {
1263
+ orgChart.zoomStartHandler({
1264
+ orgChart: orgChart,
1265
+ originalEvent: event
1266
+ });
1267
+ };
1268
+ zoomingBoundHandler = function (event) {
1269
+ orgChart.zoomingHandler({
1270
+ orgChart: orgChart,
1271
+ originalEvent: event
1272
+ });
1273
+ };
1274
+ zoomEndBoundHandler = function () {
1275
+ orgChart.zoomEndHandler({
1276
+ orgChart: orgChart
1277
+ });
1278
+ };
1279
+
1280
+ setState(chartElement, 'zoomWheelBoundHandler', zoomWheelBoundHandler);
1281
+ setState(chartElement, 'zoomStartBoundHandler', zoomStartBoundHandler);
1282
+ setState(chartElement, 'zoomingBoundHandler', zoomingBoundHandler);
1283
+ setState(chartElement, 'zoomEndBoundHandler', zoomEndBoundHandler);
1284
+ chartContainerElement.addEventListener('wheel', zoomWheelBoundHandler);
1285
+ chartContainerElement.addEventListener('touchstart', zoomStartBoundHandler);
1286
+ getDocument().addEventListener('touchmove', zoomingBoundHandler);
1287
+ getDocument().addEventListener('touchend', zoomEndBoundHandler);
1288
+ },
1289
+ unbindZoom: function () {
1290
+ const chartContainerElement = this.chartContainer;
1291
+ const chartElement = this.chart;
1292
+ let zoomWheelBoundHandler;
1293
+ let zoomStartBoundHandler;
1294
+ let zoomingBoundHandler;
1295
+ let zoomEndBoundHandler;
1296
+
1297
+ if (!chartContainerElement || !chartElement) {
1298
+ return;
1299
+ }
1300
+
1301
+ zoomWheelBoundHandler = getState(chartElement, 'zoomWheelBoundHandler');
1302
+ zoomStartBoundHandler = getState(chartElement, 'zoomStartBoundHandler');
1303
+ zoomingBoundHandler = getState(chartElement, 'zoomingBoundHandler');
1304
+ zoomEndBoundHandler = getState(chartElement, 'zoomEndBoundHandler');
1305
+ if (zoomWheelBoundHandler) {
1306
+ chartContainerElement.removeEventListener('wheel', zoomWheelBoundHandler);
1307
+ setState(chartElement, 'zoomWheelBoundHandler', null);
1308
+ }
1309
+ if (zoomStartBoundHandler) {
1310
+ chartContainerElement.removeEventListener('touchstart', zoomStartBoundHandler);
1311
+ setState(chartElement, 'zoomStartBoundHandler', null);
1312
+ }
1313
+ if (zoomingBoundHandler) {
1314
+ getDocument().removeEventListener('touchmove', zoomingBoundHandler);
1315
+ setState(chartElement, 'zoomingBoundHandler', null);
1316
+ }
1317
+ if (zoomEndBoundHandler) {
1318
+ getDocument().removeEventListener('touchend', zoomEndBoundHandler);
1319
+ setState(chartElement, 'zoomEndBoundHandler', null);
1320
+ }
1321
+ },
1322
+ //
1323
+ getPinchDist: function (e) {
1324
+ const nativeEvent = e.originalEvent || e;
1325
+
1326
+ return Math.sqrt((nativeEvent.touches[0].clientX - nativeEvent.touches[1].clientX) * (nativeEvent.touches[0].clientX - nativeEvent.touches[1].clientX) +
1327
+ (nativeEvent.touches[0].clientY - nativeEvent.touches[1].clientY) * (nativeEvent.touches[0].clientY - nativeEvent.touches[1].clientY));
1328
+ },
1329
+ getPinchCenter: function (e) {
1330
+ const nativeEvent = e.originalEvent || e;
1331
+
1332
+ return {
1333
+ x: (nativeEvent.touches[0].clientX + nativeEvent.touches[1].clientX) / 2,
1334
+ y: (nativeEvent.touches[0].clientY + nativeEvent.touches[1].clientY) / 2
1335
+ };
1336
+ },
1337
+ //
1338
+ setChartScale: function (chartEl, newScale, zoomAnchor) {
1339
+ const chartElement = getElement(chartEl);
1340
+ const chartContainerElement = this.chartContainer;
1341
+ let opts;
1342
+ let chartRect;
1343
+ let currentScale = 1;
1344
+ let currentTranslateX = 0;
1345
+ let currentTranslateY = 0;
1346
+ let targetScale = 1;
1347
+ let anchorX;
1348
+ let anchorY;
1349
+ let localAnchorX;
1350
+ let localAnchorY;
1351
+ let baseLeft;
1352
+ let baseTop;
1353
+ let containerRect;
1354
+ let targetTranslateX;
1355
+ let targetTranslateY;
1356
+
1357
+ if (!chartElement) {
1358
+ return;
1359
+ }
1360
+
1361
+ opts = getState(chartElement, 'options');
1362
+ currentScale = getChartTransform(chartElement).scale;
1363
+ currentTranslateX = getChartTransform(chartElement).translateX;
1364
+ currentTranslateY = getChartTransform(chartElement).translateY;
1365
+
1366
+ targetScale = currentScale * newScale;
1367
+ if (!(targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit)) {
1368
+ return;
1369
+ }
1370
+
1371
+ targetTranslateX = currentTranslateX;
1372
+ targetTranslateY = currentTranslateY;
1373
+ chartRect = typeof chartElement.getBoundingClientRect === 'function'
1374
+ ? chartElement.getBoundingClientRect()
1375
+ : null;
1376
+ if (!zoomAnchor && chartContainerElement && typeof chartContainerElement.getBoundingClientRect === 'function') {
1377
+ containerRect = chartContainerElement.getBoundingClientRect();
1378
+ zoomAnchor = {
1379
+ x: containerRect.left + (containerRect.width / 2),
1380
+ y: containerRect.top + (containerRect.height / 2)
1381
+ };
1382
+ }
1383
+ if (zoomAnchor && chartRect && typeof zoomAnchor.x === 'number' && typeof zoomAnchor.y === 'number' && currentScale !== 0) {
1384
+ anchorX = zoomAnchor.x;
1385
+ anchorY = zoomAnchor.y;
1386
+ localAnchorX = (anchorX - chartRect.left) / currentScale;
1387
+ localAnchorY = (anchorY - chartRect.top) / currentScale;
1388
+ baseLeft = chartRect.left - currentTranslateX;
1389
+ baseTop = chartRect.top - currentTranslateY;
1390
+ targetTranslateX = anchorX - baseLeft - (localAnchorX * targetScale);
1391
+ targetTranslateY = anchorY - baseTop - (localAnchorY * targetScale);
1392
+ }
1393
+
1394
+ chartElement.style.transformOrigin = '0 0';
1395
+ chartElement.style.transform = 'matrix(' + targetScale + ', 0, 0, ' + targetScale + ', ' + targetTranslateX + ', ' + targetTranslateY + ')';
1396
+ this.updateMinimap();
1397
+ },
1398
+ //
1399
+ buildJsonDS: function (liEl) {
1400
+ const orgChart = this;
1401
+ const liElement = getElement(liEl);
1402
+ let childListElement;
1403
+ let jsonNodeData;
1404
+
1405
+ if (!liElement) {
1406
+ return {};
1407
+ }
1408
+
1409
+ childListElement = Array.from(liElement.children || []).find(function (childEl) {
1410
+ return childEl.tagName === 'UL';
1411
+ }) || null;
1412
+
1413
+ jsonNodeData = {
1414
+ 'name': ((liElement.childNodes && liElement.childNodes[0]) ? liElement.childNodes[0].textContent : '').trim(),
1415
+ 'relationship': (liElement.parentElement && liElement.parentElement.parentElement && liElement.parentElement.parentElement.tagName === 'LI' ? '1' : '0')
1416
+ + (liElement.parentElement ? Array.from(liElement.parentElement.children).filter(function (siblingEl) {
1417
+ return siblingEl !== liElement && siblingEl.tagName === 'LI';
1418
+ }).length ? 1 : 0 : 0)
1419
+ + (childListElement ? 1 : 0)
1420
+ };
1421
+ forEachValue(getDatasetSnapshot(liElement), function(key, value) {
1422
+ jsonNodeData[key] = value;
1423
+ });
1424
+ if (childListElement) {
1425
+ Array.from(childListElement.children).forEach(function (childEl) {
1426
+ if (childEl.tagName !== 'LI') {
1427
+ return;
1428
+ }
1429
+ if (!jsonNodeData.children) {
1430
+ jsonNodeData.children = [];
1431
+ }
1432
+ jsonNodeData.children.push(orgChart.buildJsonDS(childEl));
1433
+ });
1434
+ }
1435
+
1436
+ return jsonNodeData;
1437
+ },
1438
+ // process datasource and add necessary information
1439
+ attachRel: function (data, flags) {
1440
+ const orgChart = this;
1441
+ data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0);
1442
+ if (this.options?.compact?.constructor === Function && this.options.compact(data)) {
1443
+ data.compact = true;
1444
+ }
1445
+ if (data.children) {
1446
+ data.children.forEach(function(item) {
1447
+ if (data.hybrid || data.vertical) { // identify all the descendant nodes except the root node of hybrid structure
1448
+ item.vertical = true;
1449
+ } else if (data.compact && item.children) { // identify all the compact ancestor nodes
1450
+ item.compact = true;
1451
+ } else if (data.compact && !item.children) { // identify all the compact descendant nodes
1452
+ item.associatedCompact = true;
1453
+ }
1454
+ orgChart.attachRel(item, '1' + (data.children.length > 1 ? 1 : 0));
1455
+ });
1456
+ }
1457
+ return data;
1458
+ },
1459
+ //
1460
+ loopChart: function (chartEl, includeNodeData) {
1461
+ const orgChart = this;
1462
+ const chartElement = getElement(chartEl);
1463
+ let nodeElement;
1464
+ let childNodesContainerElement;
1465
+ let hierarchyNodeData;
1466
+
1467
+ includeNodeData = (includeNodeData !== null && includeNodeData !== undefined) ? includeNodeData : false;
1468
+
1469
+ if (!chartElement) {
1470
+ return {};
1471
+ }
1472
+
1473
+ nodeElement = chartElement.classList && chartElement.classList.contains('hierarchy')
1474
+ ? Array.from(chartElement.children || []).find(function (childEl) {
1475
+ return childEl.classList && childEl.classList.contains('node');
1476
+ })
1477
+ : chartElement.querySelector('.node');
1478
+ if (!nodeElement) {
1479
+ return {};
1480
+ }
1481
+
1482
+ hierarchyNodeData = { 'id': nodeElement.id };
1483
+ if (includeNodeData) {
1484
+ forEachValue(getState(nodeElement, 'nodeData'), function (key, value) {
1485
+ hierarchyNodeData[key] = value;
1486
+ });
1487
+ }
1488
+ childNodesContainerElement = Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
1489
+ return childEl !== nodeElement && childEl.classList && childEl.classList.contains('nodes');
1490
+ }) || null;
1491
+ if (childNodesContainerElement) {
1492
+ Array.from(childNodesContainerElement.children).forEach(function (childEl) {
1493
+ if (!childEl.classList || !childEl.classList.contains('hierarchy')) {
1494
+ return;
1495
+ }
1496
+ if (!hierarchyNodeData.children) {
1497
+ hierarchyNodeData.children = [];
1498
+ }
1499
+ hierarchyNodeData.children.push(orgChart.loopChart(childEl, includeNodeData));
1500
+ });
1501
+ }
1502
+
1503
+ return hierarchyNodeData;
1504
+ },
1505
+ //
1506
+ getHierarchy: function (includeNodeData) {
1507
+ const chartElement = this.chart;
1508
+ let nodeElements;
1509
+
1510
+ includeNodeData = (includeNodeData !== null && includeNodeData !== undefined) ? includeNodeData : false;
1511
+ if (!chartElement) {
1512
+ return 'Error: orgchart does not exist'
1513
+ } else {
1514
+ nodeElements = chartElement ? Array.from(chartElement.querySelectorAll('.node')) : [];
1515
+ if (!nodeElements.length) {
1516
+ return 'Error: nodes do not exist'
1517
+ } else {
1518
+ if (nodeElements.some(function (nodeEl) { return !nodeEl.id; })) {
1519
+ return 'Error: All nodes of orghcart to be exported must have data-id attribute!';
1520
+ }
1521
+ }
1522
+ }
1523
+ return this.loopChart(chartElement, includeNodeData);
1524
+ },
1525
+ // detect the exist/display state of related node
1526
+ getNodeState: function (nodeEl, relation) {
1527
+ const nodeElement = getElement(nodeEl);
1528
+ let relatedElement;
1529
+ let isVerticalNode;
1530
+ let siblingElements;
1531
+
1532
+ if (!nodeElement || !nodeElement.classList || !nodeElement.classList.contains('node')) {
1533
+ return { 'exist': false, 'visible': false };
1534
+ }
1535
+
1536
+ relation = relation || 'self';
1537
+ isVerticalNode = !!nodeElement.closest('vertical');
1538
+
1539
+ if (relation === 'parent') {
1540
+ if (isVerticalNode) {
1541
+ relatedElement = nodeElement.closest('ul');
1542
+ relatedElement = relatedElement ? relatedElement.parentElement && relatedElement.parentElement.closest('ul') : null;
1543
+ if (!relatedElement) {
1544
+ relatedElement = nodeElement.closest('.nodes');
1545
+ if (!relatedElement) {
1546
+ relatedElement = nodeElement.closest('.vertical');
1547
+ relatedElement = relatedElement && relatedElement.parentElement ? relatedElement.parentElement.firstElementChild : null;
1548
+ }
1549
+ }
1550
+ } else {
1551
+ relatedElement = nodeElement.closest('.nodes');
1552
+ relatedElement = relatedElement ? relatedElement.previousElementSibling : null;
1553
+ }
1554
+
1555
+ if (relatedElement) {
1556
+ if (relatedElement.classList.contains('hidden')
1557
+ || (relatedElement.closest('.nodes') && relatedElement.closest('.nodes').classList.contains('hidden'))
1558
+ || (relatedElement.closest('.vertical') && relatedElement.closest('.vertical').classList.contains('hidden'))) {
1559
+ return { 'exist': true, 'visible': false };
1560
+ }
1561
+ return { 'exist': true, 'visible': true };
1562
+ }
1563
+ } else if (relation === 'children') {
1564
+ relatedElement = isVerticalNode
1565
+ ? Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
1566
+ return childEl.tagName === 'UL';
1567
+ }) || null
1568
+ : Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
1569
+ return childEl !== nodeElement && childEl.classList && childEl.classList.contains('nodes');
1570
+ }) || null;
1571
+
1572
+ if (relatedElement) {
1573
+ if (!relatedElement.classList.contains('hidden')) {
1574
+ return { 'exist': true, 'visible': true };
1575
+ }
1576
+ return { 'exist': true, 'visible': false };
1577
+ }
1578
+ } else if (relation === 'siblings') {
1579
+ if (isVerticalNode) {
1580
+ relatedElement = nodeElement.closest('ul');
1581
+ siblingElements = relatedElement ? Array.from(relatedElement.children || []).filter(function (childEl) {
1582
+ return childEl.tagName === 'LI';
1583
+ }) : [];
1584
+ } else {
1585
+ siblingElements = nodeElement.parentElement && nodeElement.parentElement.parentElement
1586
+ ? Array.from(nodeElement.parentElement.parentElement.children).filter(function (childEl) {
1587
+ return childEl !== nodeElement.parentElement;
1588
+ })
1589
+ : [];
1590
+ relatedElement = siblingElements[0] || null;
1591
+ }
1592
+
1593
+ if (relatedElement && (!isVerticalNode || siblingElements.length > 1)) {
1594
+ if (!siblingElements.some(function (siblingEl) { return siblingEl.classList.contains('hidden'); })
1595
+ && !siblingElements.some(function (siblingEl) { return siblingEl.parentElement && siblingEl.parentElement.classList.contains('hidden'); })
1596
+ && (!isVerticalNode || !relatedElement.closest('.vertical') || !relatedElement.closest('.vertical').classList.contains('hidden'))) {
1597
+ return { 'exist': true, 'visible': true };
1598
+ }
1599
+ return { 'exist': true, 'visible': false };
1600
+ }
1601
+ } else {
1602
+ if (!((nodeElement.closest('.nodes') && nodeElement.closest('.nodes').classList.contains('hidden')) ||
1603
+ (nodeElement.closest('.hierarchy') && nodeElement.closest('.hierarchy').classList.contains('hidden')) ||
1604
+ (nodeElement.closest('.vertical') && ((nodeElement.closest('.nodes') && nodeElement.closest('.nodes').classList.contains('hidden')) || nodeElement.closest('.vertical').classList.contains('hidden')))
1605
+ )) {
1606
+ return { 'exist': true, 'visible': true };
1607
+ }
1608
+ return { 'exist': true, 'visible': false };
1609
+ }
1610
+
1611
+ return { 'exist': false, 'visible': false };
1612
+ },
1613
+ getParent: function (nodeEl) {
1614
+ return this.getRelatedNodes(nodeEl, 'parent');
1615
+ },
1616
+ getChildren: function (nodeEl) {
1617
+ return this.getRelatedNodes(nodeEl, 'children');
1618
+ },
1619
+ getSiblings: function (nodeEl) {
1620
+ return this.getRelatedNodes(nodeEl, 'siblings');
1621
+ },
1622
+ // find the related nodes
1623
+ getRelatedNodes: function (nodeEl, relation) {
1624
+ const nodeElement = getElement(nodeEl);
1625
+ let nodesContainerEl;
1626
+ let childNodesContainerElement;
1627
+ let hierarchyEl;
1628
+
1629
+ if (!nodeElement || !nodeElement.classList || !nodeElement.classList.contains('node')) {
1630
+ return relation === 'parent' ? null : [];
1631
+ }
1632
+
1633
+ if (relation === 'parent') {
1634
+ nodesContainerEl = nodeElement.closest('.nodes');
1635
+ return nodesContainerEl ? nodesContainerEl.previousElementSibling : null;
1636
+ } else if (relation === 'children') {
1637
+ childNodesContainerElement = Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
1638
+ return childEl !== nodeElement && childEl.classList && childEl.classList.contains('nodes');
1639
+ });
1640
+
1641
+ if (!childNodesContainerElement) {
1642
+ return [];
1643
+ }
1644
+
1645
+ return Array.from(childNodesContainerElement.children).filter(function (childEl) {
1646
+ return childEl.classList && childEl.classList.contains('hierarchy');
1647
+ }).map(function (childHierarchyEl) {
1648
+ return childHierarchyEl.querySelector('.node');
1649
+ }).filter(Boolean);
1650
+ } else if (relation === 'siblings') {
1651
+ hierarchyEl = nodeElement.closest('.hierarchy');
1652
+ if (!hierarchyEl || !hierarchyEl.parentElement) {
1653
+ return [];
1654
+ }
1655
+
1656
+ return Array.from(hierarchyEl.parentElement.children).filter(function (siblingEl) {
1657
+ return siblingEl !== hierarchyEl;
1658
+ }).map(function (siblingHierarchyEl) {
1659
+ return siblingHierarchyEl.querySelector('.node');
1660
+ }).filter(Boolean);
1661
+ }
1662
+
1663
+ return [];
1664
+ },
1665
+ hideParentEnd: function (event) {
1666
+ const nativeEvent = event.originalEvent || event;
1667
+ const animatedNodeElement = getElement(event.animatedNode || nativeEvent.animatedNode || nativeEvent.target);
1668
+ const parentElement = getElement(event.parent || nativeEvent.parent);
1669
+
1670
+ if (animatedNodeElement && animatedNodeElement.classList) {
1671
+ animatedNodeElement.classList.remove('sliding');
1672
+ }
1673
+ if (parentElement && parentElement.classList) {
1674
+ parentElement.classList.add('hidden');
1675
+ }
1676
+ },
1677
+ // recursively hide the ancestor node and sibling nodes of the specified node
1678
+ hideParent: function (nodeEl) {
1679
+ const nodeElement = getElement(nodeEl);
1680
+ let parentEl;
1681
+ let chartElement;
1682
+ let hierarchyElement;
1683
+
1684
+ if (!nodeElement) {
1685
+ return;
1686
+ }
1687
+
1688
+ parentEl = this.getParent(nodeElement);
1689
+ chartElement = nodeElement.closest('.orgchart');
1690
+ hierarchyElement = nodeElement.closest('.hierarchy');
1691
+ if (parentEl && parentEl.querySelector && parentEl.querySelector('.spinner')) {
1692
+ if (chartElement) {
1693
+ setState(chartElement, 'inAjax', false);
1694
+ }
1695
+ }
1696
+
1697
+ if (this.getNodeState(nodeElement, 'siblings').visible) {
1698
+ this.hideSiblings(nodeElement);
1699
+ }
1700
+
1701
+ if (hierarchyElement) {
1702
+ hierarchyElement.classList.add('isAncestorsCollapsed');
1703
+ }
1704
+ if (this.getNodeState(parentEl).visible) {
1705
+ parentEl.classList.add('sliding', 'slide-down');
1706
+ parentEl.addEventListener('transitionend', function (event) {
1707
+ event.animatedNode = parentEl;
1708
+ event.parent = parentEl;
1709
+ OrgChart.prototype.hideParentEnd(event);
1710
+ }, { once: true });
1711
+ }
1712
+
1713
+ if (this.getNodeState(parentEl, 'parent').visible) {
1714
+ this.hideParent(parentEl);
1715
+ }
1716
+ },
1717
+ showParentEnd: function (event) {
1718
+ const nativeEvent = event.originalEvent || event;
1719
+ const animatedNodeElement = getElement(event.animatedNode || nativeEvent.animatedNode || nativeEvent.target);
1720
+ const nodeElement = getElement(event.node || nativeEvent.node);
1721
+ let topEdgeElement;
1722
+
1723
+ if (animatedNodeElement && animatedNodeElement.classList) {
1724
+ animatedNodeElement.classList.remove('sliding');
1725
+ }
1726
+ if (this.isInAction(nodeElement)) {
1727
+ topEdgeElement = nodeElement ? Array.from(nodeElement.children || []).find(function (childEl) {
1728
+ return childEl.classList && childEl.classList.contains('topEdge');
1729
+ }) || null : null;
1730
+ this.switchVerticalArrow(topEdgeElement);
1731
+ }
1732
+ },
1733
+ // show the parent node of the specified node
1734
+ showParent: function (nodeEl) {
1735
+ const orgChart = this;
1736
+ const nodeElement = getElement(nodeEl);
1737
+ let parentEl;
1738
+ let hierarchyElement;
1739
+
1740
+ if (!nodeElement) {
1741
+ return;
1742
+ }
1743
+
1744
+ parentEl = this.getParent(nodeElement);
1745
+ hierarchyElement = nodeElement.closest('.hierarchy');
1746
+ if (parentEl && parentEl.classList) {
1747
+ parentEl.classList.remove('hidden');
1748
+ }
1749
+ if (hierarchyElement) {
1750
+ hierarchyElement.classList.remove('isAncestorsCollapsed');
1751
+ }
1752
+ this.repaint(parentEl);
1753
+ if (parentEl && parentEl.classList) {
1754
+ parentEl.classList.add('sliding');
1755
+ parentEl.classList.remove('slide-down');
1756
+ parentEl.addEventListener('transitionend', function (event) {
1757
+ event.animatedNode = parentEl;
1758
+ event.node = nodeElement;
1759
+ orgChart.showParentEnd(event);
1760
+ }, { once: true });
1761
+ }
1762
+ },
1763
+ stopAjax: function (nodeLevelEl) {
1764
+ const nodeLevelElement = getElement(nodeLevelEl);
1765
+ let chartElement;
1766
+
1767
+ if (!nodeLevelElement || !nodeLevelElement.querySelector('.spinner')) {
1768
+ return;
1769
+ }
1770
+
1771
+ chartElement = nodeLevelElement.closest('.orgchart');
1772
+ if (chartElement) {
1773
+ setState(chartElement, 'inAjax', false);
1774
+ }
1775
+ },
1776
+ isVisibleNode: function (index, elem) {
1777
+ return this.getNodeState(elem).visible;
1778
+ },
1779
+ isCompactDescendant: function (index, elem) {
1780
+ return !!(elem && elem.parentElement && elem.parentElement.matches('.node.compact'));
1781
+ },
1782
+ // do some necessary cleanup tasks when hide animation is finished
1783
+ hideChildrenEnd: function (event) {
1784
+ const nativeEvent = event.originalEvent || event;
1785
+ const nodeElement = getElement(event.node || nativeEvent.node);
1786
+ const animatedNodeElements = event.animatedNodes || nativeEvent.animatedNodes;
1787
+ let bottomEdgeElement;
1788
+
1789
+ getElements(animatedNodeElements).forEach(function (animatedNodeEl) {
1790
+ let lowerNodesElement;
1791
+
1792
+ if (animatedNodeEl.classList) {
1793
+ animatedNodeEl.classList.remove('sliding');
1794
+ }
1795
+ lowerNodesElement = animatedNodeEl && typeof animatedNodeEl.closest === 'function'
1796
+ ? animatedNodeEl.closest('.nodes')
1797
+ : null;
1798
+ if (lowerNodesElement && lowerNodesElement.classList) {
1799
+ lowerNodesElement.classList.add('hidden');
1800
+ }
1801
+ });
1802
+ if (this.isInAction(nodeElement)) {
1803
+ bottomEdgeElement = nodeElement ? Array.from(nodeElement.children || []).find(function (childEl) {
1804
+ return childEl.classList && childEl.classList.contains('bottomEdge');
1805
+ }) || null : null;
1806
+ this.switchVerticalArrow(bottomEdgeElement);
1807
+ }
1808
+ },
1809
+ // recursively hide the descendant nodes of the specified node
1810
+ hideChildren: function (nodeEl) {
1811
+ const orgChart = this;
1812
+ const nodeElement = getElement(nodeEl);
1813
+ let hierarchyElement;
1814
+ let lowerLevelElement;
1815
+ let animatedNodeElements;
1816
+ let isVerticalDesc;
1817
+
1818
+ if (!nodeElement) {
1819
+ return;
1820
+ }
1821
+
1822
+ hierarchyElement = nodeElement.closest('.hierarchy');
1823
+ lowerLevelElement = Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
1824
+ return childEl !== nodeElement && childEl.classList && childEl.classList.contains('nodes');
1825
+ }) || null;
1826
+
1827
+ if (!lowerLevelElement) {
1828
+ return;
1829
+ }
1830
+
1831
+ if (hierarchyElement) {
1832
+ hierarchyElement.classList.add('isChildrenCollapsed');
1833
+ }
1834
+ this.stopAjax(lowerLevelElement);
1835
+ animatedNodeElements = Array.from(lowerLevelElement.querySelectorAll('.node')).filter(function (visibleNodeEl, index) {
1836
+ return this.isVisibleNode(index, visibleNodeEl) && !this.isCompactDescendant(index, visibleNodeEl);
1837
+ }, this);
1838
+ isVerticalDesc = lowerLevelElement.classList.contains('vertical');
1839
+ if (!isVerticalDesc) {
1840
+ animatedNodeElements.forEach(function (animatedNodeEl) {
1841
+ const animatedHierarchyElement = animatedNodeEl.closest('.hierarchy');
1842
+
1843
+ if (animatedHierarchyElement) {
1844
+ animatedHierarchyElement.classList.add('isCollapsedDescendant');
1845
+ }
1846
+ });
1847
+ }
1848
+ if (lowerLevelElement.classList.contains('vertical') || lowerLevelElement.querySelector('.vertical')) {
1849
+ animatedNodeElements.forEach(function (animatedNodeEl) {
1850
+ Array.from(animatedNodeEl.querySelectorAll('.' + this.options.icons.expanded)).forEach(function (expandedIconEl) {
1851
+ expandedIconEl.classList.remove(this.options.icons.expanded);
1852
+ expandedIconEl.classList.add(this.options.icons.collapsed);
1853
+ }, this);
1854
+ }, this);
1855
+ }
1856
+ this.repaint(animatedNodeElements[0]);
1857
+ animatedNodeElements.forEach(function (animatedNodeEl) {
1858
+ animatedNodeEl.classList.add('sliding', 'slide-up');
1859
+ });
1860
+ if (animatedNodeElements[0]) {
1861
+ animatedNodeElements[0].addEventListener('transitionend', function (event) {
1862
+ event.node = nodeElement;
1863
+ event.animatedNodes = animatedNodeElements;
1864
+ orgChart.hideChildrenEnd(event);
1865
+ }, { once: true });
1866
+ }
1867
+ },
1868
+ //
1869
+ showChildrenEnd: function (event) {
1870
+ const nativeEvent = event.originalEvent || event;
1871
+ const nodeElement = getElement(event.node || nativeEvent.node);
1872
+ const animatedNodeElements = event.animatedNodes || nativeEvent.animatedNodes;
1873
+ let bottomEdgeElement;
1874
+
1875
+ getElements(animatedNodeElements).forEach(function (animatedNodeEl) {
1876
+ if (animatedNodeEl.classList) {
1877
+ animatedNodeEl.classList.remove('sliding');
1878
+ }
1879
+ });
1880
+ if (this.isInAction(nodeElement)) {
1881
+ bottomEdgeElement = nodeElement ? Array.from(nodeElement.children || []).find(function (childEl) {
1882
+ return childEl.classList && childEl.classList.contains('bottomEdge');
1883
+ }) || null : null;
1884
+ this.switchVerticalArrow(bottomEdgeElement);
1885
+ }
1886
+ },
1887
+ // show the children nodes of the specified node
1888
+ showChildren: function (nodeEl) {
1889
+ const orgChart = this;
1890
+ const nodeElement = getElement(nodeEl);
1891
+ let hierarchyElement;
1892
+ let levelsElement;
1893
+ let isVerticalDesc;
1894
+ let animatedNodeElements;
1895
+
1896
+ if (!nodeElement) {
1897
+ return;
1898
+ }
1899
+
1900
+ hierarchyElement = nodeElement.closest('.hierarchy');
1901
+ levelsElement = Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
1902
+ return childEl !== nodeElement && childEl.classList && childEl.classList.contains('nodes');
1903
+ }) || null;
1904
+
1905
+ if (!levelsElement) {
1906
+ return;
1907
+ }
1908
+
1909
+ if (hierarchyElement) {
1910
+ hierarchyElement.classList.remove('isChildrenCollapsed');
1911
+ }
1912
+ levelsElement.classList.remove('hidden');
1913
+ isVerticalDesc = levelsElement.classList.contains('vertical');
1914
+ animatedNodeElements = isVerticalDesc
1915
+ ? Array.from(levelsElement.querySelectorAll('.node')).filter(function (visibleNodeEl, index) {
1916
+ return this.isVisibleNode(index, visibleNodeEl);
1917
+ }, this)
1918
+ : Array.from(levelsElement.children).filter(function (childEl) {
1919
+ return childEl.classList && childEl.classList.contains('hierarchy');
1920
+ }).map(function (childHierarchyEl) {
1921
+ return childHierarchyEl.querySelector('.node');
1922
+ }).filter(function (visibleNodeEl, index) {
1923
+ return this.isVisibleNode(index, visibleNodeEl);
1924
+ }, this);
1925
+ if (!isVerticalDesc) {
1926
+ animatedNodeElements.forEach(function (animatedNodeEl) {
1927
+ const animatedHierarchyElement = animatedNodeEl.closest('.hierarchy');
1928
+
1929
+ if (animatedHierarchyElement && !animatedNodeEl.matches(':only-child')) {
1930
+ animatedHierarchyElement.classList.add('isChildrenCollapsed');
1931
+ }
1932
+ if (animatedHierarchyElement) {
1933
+ animatedHierarchyElement.classList.remove('isCollapsedDescendant');
1934
+ }
1935
+ });
1936
+ }
1937
+ this.repaint(animatedNodeElements[0]);
1938
+ animatedNodeElements.forEach(function (animatedNodeEl) {
1939
+ animatedNodeEl.classList.add('sliding');
1940
+ animatedNodeEl.classList.remove('slide-up');
1941
+ });
1942
+ if (animatedNodeElements[0]) {
1943
+ animatedNodeElements[0].addEventListener('transitionend', function (event) {
1944
+ event.node = nodeElement;
1945
+ event.animatedNodes = animatedNodeElements;
1946
+ orgChart.showChildrenEnd(event);
1947
+ }, { once: true });
1948
+ }
1949
+ },
1950
+ //
1951
+ hideSiblingsEnd: function (event) {
1952
+ const orgChart = this;
1953
+ const nativeEvent = event.originalEvent || event;
1954
+ const nodeElement = getElement(event.node || nativeEvent.node);
1955
+ const nodeContainerElement = getElement(event.nodeContainer || nativeEvent.nodeContainer);
1956
+ let direction = typeof event.direction !== 'undefined' ? event.direction : nativeEvent.direction;
1957
+ const animatedNodeElements = event.animatedNodes || nativeEvent.animatedNodes;
1958
+
1959
+ if (typeof direction === 'undefined') {
1960
+ direction = nativeEvent.direction;
1961
+ }
1962
+ let siblingElements = [];
1963
+ let currentSiblingElement;
1964
+
1965
+ if (nodeContainerElement) {
1966
+ if (direction === 'left') {
1967
+ currentSiblingElement = nodeContainerElement.previousElementSibling;
1968
+ while (currentSiblingElement) {
1969
+ if (!currentSiblingElement.classList.contains('hidden')) {
1970
+ siblingElements.push(currentSiblingElement);
1971
+ }
1972
+ currentSiblingElement = currentSiblingElement.previousElementSibling;
1973
+ }
1974
+ } else if (direction === 'right') {
1975
+ currentSiblingElement = nodeContainerElement.nextElementSibling;
1976
+ while (currentSiblingElement) {
1977
+ if (!currentSiblingElement.classList.contains('hidden')) {
1978
+ siblingElements.push(currentSiblingElement);
1979
+ }
1980
+ currentSiblingElement = currentSiblingElement.nextElementSibling;
1981
+ }
1982
+ } else if (nodeContainerElement.parentElement) {
1983
+ siblingElements = Array.from(nodeContainerElement.parentElement.children).filter(function (childEl) {
1984
+ return childEl !== nodeContainerElement;
1985
+ });
1986
+ }
1987
+ }
1988
+
1989
+ getElements(animatedNodeElements).forEach(function (animatedNodeEl) {
1990
+ if (animatedNodeEl.classList) {
1991
+ animatedNodeEl.classList.remove('sliding');
1992
+ }
1993
+ });
1994
+ siblingElements.forEach(function (siblingElement) {
1995
+ Array.from(siblingElement.querySelectorAll('.node')).filter(function (siblingNodeEl) {
1996
+ return orgChart.isVisibleNode(null, siblingNodeEl);
1997
+ }).slice(1)
1998
+ .forEach(function (siblingNodeEl) {
1999
+ siblingNodeEl.classList.remove('slide-left', 'slide-right');
2000
+ if (!orgChart.options.compact) {
2001
+ siblingNodeEl.classList.add('slide-up');
2002
+ }
2003
+ });
2004
+ Array.from(siblingElement.querySelectorAll('.nodes, .vertical')).forEach(function (descendantEl) {
2005
+ descendantEl.classList.add('hidden');
2006
+ });
2007
+ siblingElement.classList.add('hidden');
2008
+ });
2009
+
2010
+ if (this.isInAction(nodeElement)) {
2011
+ this.switchHorizontalArrow(nodeElement);
2012
+ }
2013
+ },
2014
+ // hide the sibling nodes of the specified node
2015
+ hideSiblings: function (nodeEl, direction) {
2016
+ const orgChart = this;
2017
+ const nodeElement = getElement(nodeEl);
2018
+ let chartElement;
2019
+ let nodeContainerElement;
2020
+ let siblingContainerElements = [];
2021
+ let animatedNodeElements = [];
2022
+
2023
+ if (!nodeElement) {
2024
+ return;
2025
+ }
2026
+
2027
+ chartElement = nodeElement.closest('.orgchart');
2028
+ nodeContainerElement = nodeElement.closest('.hierarchy');
2029
+ if (!nodeContainerElement) {
2030
+ return;
2031
+ }
2032
+
2033
+ nodeContainerElement.classList.add('isSiblingsCollapsed');
2034
+ siblingContainerElements = nodeContainerElement.parentElement
2035
+ ? Array.from(nodeContainerElement.parentElement.children).filter(function (childEl) {
2036
+ return childEl !== nodeContainerElement;
2037
+ })
2038
+ : [];
2039
+
2040
+ if (siblingContainerElements.some(function (siblingContainerEl) {
2041
+ return siblingContainerEl.querySelector('.spinner');
2042
+ })) {
2043
+ if (chartElement) {
2044
+ setState(chartElement, 'inAjax', false);
2045
+ }
2046
+ }
2047
+
2048
+ if (direction) {
2049
+ if (direction === 'left') {
2050
+ let currentPreviousSiblingElement = nodeContainerElement.previousElementSibling;
2051
+
2052
+ nodeContainerElement.classList.add('left-sibs');
2053
+ siblingContainerElements.forEach(function (siblingContainerEl) {
2054
+ if (siblingContainerEl.classList.contains('isSiblingsCollapsed')) {
2055
+ siblingContainerEl.classList.remove('isSiblingsCollapsed', 'left-sibs');
2056
+ }
2057
+ });
2058
+
2059
+ while (currentPreviousSiblingElement) {
2060
+ currentPreviousSiblingElement.classList.add('isCollapsedSibling', 'isChildrenCollapsed');
2061
+ animatedNodeElements = animatedNodeElements.concat(
2062
+ Array.from(currentPreviousSiblingElement.querySelectorAll('.node')).filter(function (visibleNodeEl, index) {
2063
+ return this.isVisibleNode(index, visibleNodeEl);
2064
+ }, this)
2065
+ );
2066
+ currentPreviousSiblingElement = currentPreviousSiblingElement.previousElementSibling;
2067
+ }
2068
+ animatedNodeElements.forEach(function (animatedNodeEl) {
2069
+ animatedNodeEl.classList.add('sliding', 'slide-right');
2070
+ });
2071
+ } else {
2072
+ let currentNextSiblingElement = nodeContainerElement.nextElementSibling;
2073
+
2074
+ nodeContainerElement.classList.add('right-sibs');
2075
+ siblingContainerElements.forEach(function (siblingContainerEl) {
2076
+ if (siblingContainerEl.classList.contains('isSiblingsCollapsed')) {
2077
+ siblingContainerEl.classList.remove('isSiblingsCollapsed', 'right-sibs');
2078
+ }
2079
+ });
2080
+
2081
+ while (currentNextSiblingElement) {
2082
+ currentNextSiblingElement.classList.add('isCollapsedSibling', 'isChildrenCollapsed');
2083
+ animatedNodeElements = animatedNodeElements.concat(
2084
+ Array.from(currentNextSiblingElement.querySelectorAll('.node')).filter(function (visibleNodeEl, index) {
2085
+ return this.isVisibleNode(index, visibleNodeEl);
2086
+ }, this)
2087
+ );
2088
+ currentNextSiblingElement = currentNextSiblingElement.nextElementSibling;
2089
+ }
2090
+ animatedNodeElements.forEach(function (animatedNodeEl) {
2091
+ animatedNodeEl.classList.add('sliding', 'slide-left');
2092
+ });
2093
+ }
2094
+ } else {
2095
+ siblingContainerElements.forEach(function (siblingContainerEl) {
2096
+ const siblingAnimatedNodeElements = Array.from(siblingContainerEl.querySelectorAll('.node')).filter(function (visibleNodeEl, index) {
2097
+ return this.isVisibleNode(index, visibleNodeEl);
2098
+ }, this);
2099
+
2100
+ siblingContainerEl.classList.add('isCollapsedSibling', 'isChildrenCollapsed');
2101
+ animatedNodeElements = animatedNodeElements.concat(siblingAnimatedNodeElements);
2102
+ siblingAnimatedNodeElements.forEach(function (animatedNodeEl) {
2103
+ animatedNodeEl.classList.add('sliding');
2104
+ if (siblingContainerEl.compareDocumentPosition(nodeContainerElement) & Node.DOCUMENT_POSITION_FOLLOWING) {
2105
+ animatedNodeEl.classList.add('slide-right');
2106
+ } else {
2107
+ animatedNodeEl.classList.add('slide-left');
2108
+ }
2109
+ });
2110
+ }, this);
2111
+ }
2112
+
2113
+ if (animatedNodeElements[0]) {
2114
+ animatedNodeElements[0].addEventListener('transitionend', function (event) {
2115
+ event.node = nodeElement;
2116
+ event.nodeContainer = nodeContainerElement;
2117
+ event.direction = direction;
2118
+ event.animatedNodes = animatedNodeElements;
2119
+ orgChart.hideSiblingsEnd(event);
2120
+ }, { once: true });
2121
+ }
2122
+ },
2123
+ //
2124
+ showSiblingsEnd: function (event) {
2125
+ const nativeEvent = event.originalEvent || event;
2126
+ const nodeElement = getElement(event.node || nativeEvent.node);
2127
+ const visibleNodeElements = event.visibleNodes || nativeEvent.visibleNodes;
2128
+ let topEdgeElement;
2129
+
2130
+ getElements(visibleNodeElements).forEach(function (visibleNodeEl) {
2131
+ if (visibleNodeEl.classList) {
2132
+ visibleNodeEl.classList.remove('sliding');
2133
+ }
2134
+ });
2135
+ if (this.isInAction(nodeElement)) {
2136
+ this.switchHorizontalArrow(nodeElement);
2137
+ topEdgeElement = nodeElement ? Array.from(nodeElement.children || []).find(function (childEl) {
2138
+ return childEl.classList && childEl.classList.contains('topEdge');
2139
+ }) || null : null;
2140
+ if (topEdgeElement && topEdgeElement.classList) {
2141
+ topEdgeElement.classList.remove(this.options.icons.expandToUp);
2142
+ topEdgeElement.classList.add(this.options.icons.collapseToDown);
2143
+ }
2144
+ }
2145
+ },
2146
+ //
2147
+ showRelatedParentEnd: function(event) {
2148
+ const nativeEvent = event.originalEvent || event;
2149
+ const animatedNodeElement = getElement(event.animatedNode || nativeEvent.animatedNode || nativeEvent.target);
2150
+
2151
+ if (animatedNodeElement && animatedNodeElement.classList) {
2152
+ animatedNodeElement.classList.remove('sliding');
2153
+ }
2154
+ },
2155
+ // show the sibling nodes of the specified node
2156
+ showSiblings: function (nodeEl, direction) {
2157
+ const orgChart = this;
2158
+ const nodeElement = getElement(nodeEl);
2159
+ let nodeContainerElement;
2160
+ let siblingElements = [];
2161
+ let parentNodesContainerElement;
2162
+ let upperLevelElement;
2163
+ let visibleNodeElements;
2164
+
2165
+ if (!nodeElement) {
2166
+ return;
2167
+ }
2168
+
2169
+ // firstly, show the sibling nodes
2170
+ nodeContainerElement = nodeElement.closest('.hierarchy');
2171
+ if (!nodeContainerElement) {
2172
+ return;
2173
+ }
2174
+
2175
+ if (direction) {
2176
+ if (direction === 'left') {
2177
+ let currentPreviousSiblingElement = nodeContainerElement.previousElementSibling;
2178
+ while (currentPreviousSiblingElement) {
2179
+ currentPreviousSiblingElement.classList.remove('hidden');
2180
+ siblingElements.push(currentPreviousSiblingElement);
2181
+ currentPreviousSiblingElement = currentPreviousSiblingElement.previousElementSibling;
2182
+ }
2183
+ } else {
2184
+ let currentNextSiblingElement = nodeContainerElement.nextElementSibling;
2185
+ while (currentNextSiblingElement) {
2186
+ currentNextSiblingElement.classList.remove('hidden');
2187
+ siblingElements.push(currentNextSiblingElement);
2188
+ currentNextSiblingElement = currentNextSiblingElement.nextElementSibling;
2189
+ }
2190
+ }
2191
+ } else {
2192
+ siblingElements = nodeContainerElement.parentElement
2193
+ ? Array.from(nodeContainerElement.parentElement.children).filter(function (childEl) {
2194
+ return childEl !== nodeContainerElement;
2195
+ })
2196
+ : [];
2197
+ siblingElements.forEach(function (siblingEl) {
2198
+ siblingEl.classList.remove('hidden');
2199
+ });
2200
+ }
2201
+ // secondly, show the lines
2202
+ parentNodesContainerElement = nodeElement.closest('.nodes');
2203
+ upperLevelElement = parentNodesContainerElement ? parentNodesContainerElement.previousElementSibling : null;
2204
+ if (direction) {
2205
+ nodeContainerElement.classList.remove(direction + '-sibs');
2206
+ if (!Array.from(nodeContainerElement.classList).some(function (className) { return className.indexOf('-sibs') > -1; })) {
2207
+ nodeContainerElement.classList.remove('isSiblingsCollapsed');
2208
+ }
2209
+ siblingElements.forEach(function (siblingEl) {
2210
+ siblingEl.classList.remove('isCollapsedSibling', direction + '-sibs');
2211
+ });
2212
+ } else {
2213
+ nodeContainerElement.classList.remove('isSiblingsCollapsed');
2214
+ siblingElements.forEach(function (siblingEl) {
2215
+ siblingEl.classList.remove('isCollapsedSibling');
2216
+ });
2217
+ }
2218
+ // thirdly, show parent node if it is collapsed
2219
+ if (!this.getNodeState(nodeElement, 'parent').visible) {
2220
+ nodeContainerElement.classList.remove('isAncestorsCollapsed');
2221
+ if (upperLevelElement) {
2222
+ upperLevelElement.classList.remove('hidden');
2223
+ this.repaint(upperLevelElement);
2224
+ upperLevelElement.classList.add('sliding');
2225
+ upperLevelElement.classList.remove('slide-down');
2226
+ upperLevelElement.addEventListener('transitionend', function (event) {
2227
+ event.animatedNode = upperLevelElement;
2228
+ orgChart.showRelatedParentEnd(event);
2229
+ }, { once: true });
2230
+ }
2231
+ }
2232
+ // lastly, show the sibling nodes with animation
2233
+ visibleNodeElements = siblingElements.flatMap(function (siblingEl) {
2234
+ return Array.from(siblingEl.querySelectorAll('.node'));
2235
+ }, this).filter(function (visibleNodeEl, index) {
2236
+ return this.isVisibleNode(index, visibleNodeEl);
2237
+ }, this);
2238
+ this.repaint(visibleNodeElements[0]);
2239
+ visibleNodeElements.forEach(function (visibleNodeEl) {
2240
+ visibleNodeEl.classList.add('sliding');
2241
+ visibleNodeEl.classList.remove('slide-left', 'slide-right');
2242
+ });
2243
+ if (visibleNodeElements[0]) {
2244
+ visibleNodeElements[0].addEventListener('transitionend', function (event) {
2245
+ event.node = nodeElement;
2246
+ event.visibleNodes = visibleNodeElements;
2247
+ orgChart.showSiblingsEnd(event);
2248
+ }, { once: true });
2249
+ }
2250
+ },
2251
+ // start up loading status for requesting new nodes
2252
+ startLoading: function (edgeEl) {
2253
+ const edgeElement = getElement(edgeEl);
2254
+ const chartElement = this.chart;
2255
+ let nodeElement;
2256
+ let spinnerElement;
2257
+
2258
+ if (!edgeElement || !chartElement) {
2259
+ return false;
2260
+ }
2261
+
2262
+ if (typeof getState(chartElement, 'inAjax') !== 'undefined' && getState(chartElement, 'inAjax') === true) {
2263
+ return false;
2264
+ }
2265
+
2266
+ edgeElement.classList.add('hidden');
2267
+ nodeElement = edgeElement.parentElement;
2268
+ if (nodeElement) {
2269
+ nodeElement.insertAdjacentHTML('beforeend', `<i class="${this.options.icons.theme} ${this.options.icons.spinner} spinner"></i>`);
2270
+ spinnerElement = Array.from(nodeElement.children || []).find(function (childEl) {
2271
+ return childEl.classList && childEl.classList.contains('spinner');
2272
+ }) || null;
2273
+ Array.from(nodeElement.children || []).forEach(function (childEl) {
2274
+ if (childEl !== spinnerElement) {
2275
+ childEl.style.opacity = 0.2;
2276
+ }
2277
+ });
2278
+ }
2279
+ setState(chartElement, 'inAjax', true);
2280
+ Array.from(getDocument().querySelectorAll('.oc-export-btn')).forEach(function (exportButtonEl) {
2281
+ exportButtonEl.disabled = true;
2282
+ });
2283
+ return true;
2284
+ },
2285
+ // terminate loading status for requesting new nodes
2286
+ endLoading: function (edgeEl) {
2287
+ const edgeElement = getElement(edgeEl);
2288
+ let nodeElement;
2289
+ const chartElement = this.chart;
2290
+
2291
+ if (!edgeElement || !chartElement) {
2292
+ return;
2293
+ }
2294
+
2295
+ nodeElement = edgeElement.parentElement;
2296
+ edgeElement.classList.remove('hidden');
2297
+ if (nodeElement) {
2298
+ Array.from(nodeElement.querySelectorAll('.spinner')).forEach(function (spinnerEl) {
2299
+ spinnerEl.remove();
2300
+ });
2301
+ Array.from(nodeElement.children || []).forEach(function (childEl) {
2302
+ childEl.removeAttribute('style');
2303
+ });
2304
+ }
2305
+ setState(chartElement, 'inAjax', false);
2306
+ Array.from(getDocument().querySelectorAll('.oc-export-btn')).forEach(function (exportButtonEl) {
2307
+ exportButtonEl.disabled = false;
2308
+ });
2309
+ },
2310
+ // whether the cursor is hovering over the node
2311
+ isInAction: function (nodeEl) {
2312
+ const nodeElement = getElement(nodeEl);
2313
+ const edgeClassName = Array.from(nodeElement ? nodeElement.children : []).filter(function (childEl) {
2314
+ return childEl.classList && childEl.classList.contains('edge');
2315
+ }).map(function (edgeEl) {
2316
+ return edgeEl.className;
2317
+ }).join(' ');
2318
+
2319
+ // TODO: 展开/折叠的按钮不止4个箭头,还有toggleBtn
2320
+ return [
2321
+ this.options.icons.expandToUp,
2322
+ this.options.icons.collapseToDown,
2323
+ this.options.icons.collapseToLeft,
2324
+ this.options.icons.expandToRight
2325
+ ].some(function (icon) {
2326
+ return edgeClassName.indexOf(icon) > -1;
2327
+ });
2328
+ },
2329
+ //
2330
+ switchVerticalArrow: function (arrowEl) {
2331
+ const arrowElement = getElement(arrowEl);
2332
+
2333
+ if (!arrowElement || !arrowElement.classList) {
2334
+ return;
2335
+ }
2336
+
2337
+ arrowElement.classList.toggle(this.options.icons.expandToUp);
2338
+ arrowElement.classList.toggle(this.options.icons.collapseToDown);
2339
+ },
2340
+ //
2341
+ switchHorizontalArrow: function (nodeEl) {
2342
+ const nodeElement = getElement(nodeEl);
2343
+ const opts = this.options;
2344
+ let hierarchyElement;
2345
+ let siblingsContainerElement;
2346
+ let leftEdgeElement;
2347
+ let rightEdgeElement;
2348
+ let previousSiblingElement;
2349
+ let nextSiblingElement;
2350
+ let siblingElements;
2351
+ let sibsVisible;
2352
+
2353
+ if (!nodeElement) {
2354
+ return;
2355
+ }
2356
+
2357
+ hierarchyElement = nodeElement.closest('.hierarchy');
2358
+ siblingsContainerElement = nodeElement.closest('.nodes');
2359
+ leftEdgeElement = Array.from(nodeElement.children || []).find(function (childEl) {
2360
+ return childEl.classList && childEl.classList.contains('leftEdge');
2361
+ }) || null;
2362
+ rightEdgeElement = Array.from(nodeElement.children || []).find(function (childEl) {
2363
+ return childEl.classList && childEl.classList.contains('rightEdge');
2364
+ }) || null;
2365
+
2366
+ if (opts.toggleSiblingsResp && (typeof opts.ajaxURL === 'undefined' || getState(siblingsContainerElement, 'siblingsLoaded'))) {
2367
+ previousSiblingElement = hierarchyElement ? hierarchyElement.previousElementSibling : null;
2368
+ if (previousSiblingElement && leftEdgeElement) {
2369
+ leftEdgeElement.classList.toggle(opts.icons.collapseToLeft, previousSiblingElement.classList.contains('hidden'));
2370
+ leftEdgeElement.classList.toggle(opts.icons.expandToRight, !previousSiblingElement.classList.contains('hidden'));
2371
+ }
2372
+ nextSiblingElement = hierarchyElement ? hierarchyElement.nextElementSibling : null;
2373
+ if (nextSiblingElement && rightEdgeElement) {
2374
+ rightEdgeElement.classList.toggle(opts.icons.expandToRight, nextSiblingElement.classList.contains('hidden'));
2375
+ rightEdgeElement.classList.toggle(opts.icons.collapseToLeft, !nextSiblingElement.classList.contains('hidden'));
2376
+ }
2377
+ } else {
2378
+ siblingElements = hierarchyElement && hierarchyElement.parentElement
2379
+ ? Array.from(hierarchyElement.parentElement.children).filter(function (childEl) {
2380
+ return childEl !== hierarchyElement;
2381
+ })
2382
+ : [];
2383
+ sibsVisible = siblingElements.length ? !siblingElements[0].classList.contains('hidden') : false;
2384
+ if (leftEdgeElement) {
2385
+ leftEdgeElement.classList.toggle(opts.icons.expandToRight, sibsVisible);
2386
+ leftEdgeElement.classList.toggle(opts.icons.collapseToLeft, !sibsVisible);
2387
+ }
2388
+ if (rightEdgeElement) {
2389
+ rightEdgeElement.classList.toggle(opts.icons.collapseToLeft, sibsVisible);
2390
+ rightEdgeElement.classList.toggle(opts.icons.expandToRight, !sibsVisible);
2391
+ }
2392
+ }
2393
+ },
2394
+ //
2395
+ repaint: function (node) {
2396
+ if (node) {
2397
+ node.style.offsetWidth = node.offsetWidth;
2398
+ }
2399
+ },
2400
+ // determines how to show arrow buttons
2401
+ nodeEnterLeaveHandler: function (event) {
2402
+ const nativeEvent = event.originalEvent || event;
2403
+ const eventType = nativeEvent.type;
2404
+ const nodeElement = event.node || nativeEvent.currentTarget;
2405
+ const childElements = Array.from(nodeElement ? nodeElement.children : []);
2406
+ const isVerticalNode = !!(nodeElement && nodeElement.closest('.nodes.vertical'));
2407
+ const toggleBtnElement = childElements.find(function (childEl) {
2408
+ return childEl.classList && childEl.classList.contains('toggleBtn');
2409
+ }) || null;
2410
+ const topEdgeElement = childElements.find(function (childEl) {
2411
+ return childEl.classList && childEl.classList.contains('topEdge');
2412
+ }) || null;
2413
+ const rightEdgeElement = childElements.find(function (childEl) {
2414
+ return childEl.classList && childEl.classList.contains('rightEdge');
2415
+ }) || null;
2416
+ const bottomEdgeElement = childElements.find(function (childEl) {
2417
+ return childEl.classList && childEl.classList.contains('bottomEdge');
2418
+ }) || null;
2419
+ const leftEdgeElement = childElements.find(function (childEl) {
2420
+ return childEl.classList && childEl.classList.contains('leftEdge');
2421
+ }) || null;
2422
+ let isExpanded = false;
2423
+ if (isVerticalNode) {
2424
+ if (eventType === 'mouseenter') {
2425
+ if (toggleBtnElement) {
2426
+ isExpanded = this.getNodeState(nodeElement, 'children').visible;
2427
+ toggleBtnElement.classList.toggle(this.options.icons.collapsed, !isExpanded);
2428
+ toggleBtnElement.classList.toggle(this.options.icons.expanded, isExpanded);
2429
+ }
2430
+ } else if (toggleBtnElement) {
2431
+ toggleBtnElement.classList.remove(this.options.icons.collapsed, this.options.icons.expanded);
2432
+ }
2433
+ } else {
2434
+ if (eventType === 'mouseenter') {
2435
+ if (topEdgeElement) {
2436
+ isExpanded = this.getNodeState(nodeElement, 'parent').visible;
2437
+ topEdgeElement.classList.toggle(this.options.icons.expandToUp, !isExpanded);
2438
+ topEdgeElement.classList.toggle(this.options.icons.collapseToDown, isExpanded);
2439
+ }
2440
+ if (bottomEdgeElement) {
2441
+ isExpanded = this.getNodeState(nodeElement, 'children').visible;
2442
+ bottomEdgeElement.classList.toggle(this.options.icons.collapseToDown, !isExpanded);
2443
+ bottomEdgeElement.classList.toggle(this.options.icons.expandToUp, isExpanded);
2444
+ }
2445
+ if (leftEdgeElement || rightEdgeElement) {
2446
+ this.switchHorizontalArrow(nodeElement);
2447
+ }
2448
+ } else {
2449
+ childElements.forEach(function (childEl) {
2450
+ if (childEl.classList && childEl.classList.contains('edge')) {
2451
+ childEl.classList.remove(
2452
+ this.options.icons.expandToUp,
2453
+ this.options.icons.collapseToDown,
2454
+ this.options.icons.collapseToLeft,
2455
+ this.options.icons.expandToRight
2456
+ );
2457
+ }
2458
+ }, this);
2459
+ }
2460
+ }
2461
+ },
2462
+ //
2463
+ nodeClickHandler: function (event) {
2464
+ const chartElement = this.chart;
2465
+ const nativeEvent = event.originalEvent || event;
2466
+ const nodeElement = event.node || nativeEvent.currentTarget;
2467
+
2468
+ if (!chartElement || !nodeElement || !nodeElement.classList) {
2469
+ return;
2470
+ }
2471
+
2472
+ Array.from(chartElement.querySelectorAll('.focused')).forEach(function (focusedEl) {
2473
+ focusedEl.classList.remove('focused');
2474
+ });
2475
+ nodeElement.classList.add('focused');
2476
+ },
2477
+ addAncestors: function (data, parentId) {
2478
+ const rootHierarchyElement = this.chart ? this.chart.querySelector(':scope > .nodes > .hierarchy') : null;
2479
+ let originalRootChildren;
2480
+ let ancestorWrapperElement;
2481
+ let targetParentNodeElement;
2482
+ let targetSiblingsContainerElement;
2483
+
2484
+ if (!rootHierarchyElement) {
2485
+ return;
2486
+ }
2487
+
2488
+ this.buildHierarchy(rootHierarchyElement, data);
2489
+ originalRootChildren = Array.from(rootHierarchyElement.children).slice(0, 2);
2490
+ ancestorWrapperElement = getDocument().createElement('li');
2491
+ ancestorWrapperElement.className = 'hierarchy';
2492
+ rootHierarchyElement.insertBefore(ancestorWrapperElement, originalRootChildren[0] || null);
2493
+ originalRootChildren.forEach(function (childEl) {
2494
+ ancestorWrapperElement.appendChild(childEl);
2495
+ });
2496
+ targetParentNodeElement = getDocument().getElementById(parentId);
2497
+ targetSiblingsContainerElement = targetParentNodeElement
2498
+ ? Array.from(targetParentNodeElement.parentElement ? targetParentNodeElement.parentElement.children : []).find(function (childEl) {
2499
+ return childEl !== targetParentNodeElement && childEl.classList && childEl.classList.contains('nodes');
2500
+ }) || null
2501
+ : null;
2502
+ if (targetSiblingsContainerElement && targetSiblingsContainerElement !== ancestorWrapperElement.parentElement) {
2503
+ targetSiblingsContainerElement.appendChild(ancestorWrapperElement);
2504
+ }
2505
+ },
2506
+ addDescendants:function (data, parentEl) {
2507
+ const orgChart = this;
2508
+ const parentElement = getElement(parentEl);
2509
+ let descendantsElement;
2510
+
2511
+ if (!parentElement || !parentElement.parentElement) {
2512
+ return;
2513
+ }
2514
+
2515
+ descendantsElement = getDocument().createElement('ul');
2516
+ descendantsElement.className = 'nodes';
2517
+ parentElement.after(descendantsElement);
2518
+ forEachValue(data, function () {
2519
+ const descendantHierarchyElement = getDocument().createElement('li');
2520
+
2521
+ descendantHierarchyElement.className = 'hierarchy';
2522
+ descendantsElement.appendChild(descendantHierarchyElement);
2523
+ orgChart.buildHierarchy(descendantHierarchyElement, this);
2524
+ });
2525
+ },
2526
+ //
2527
+ HideFirstParentEnd: function (event) {
2528
+ const nativeEvent = event.originalEvent || event;
2529
+ const topEdgeElement = getElement(event.topEdge || nativeEvent.topEdge);
2530
+ const nodeElement = topEdgeElement ? topEdgeElement.parentElement : null;
2531
+
2532
+ if (this.isInAction(nodeElement)) {
2533
+ this.switchVerticalArrow(topEdgeElement);
2534
+ this.switchHorizontalArrow(nodeElement);
2535
+ }
2536
+ },
2537
+ // actions on clinking top edge of a node
2538
+ topEdgeClickHandler: function (event) {
2539
+ const orgChart = this;
2540
+ const nativeEvent = event.originalEvent || event;
2541
+ const nodeElement = event.node || nativeEvent.currentTarget;
2542
+ const topEdgeElement = getElement(event.edge || nativeEvent.target);
2543
+ const parentEl = this.getParent(nodeElement);
2544
+ const parentState = this.getNodeState(nodeElement, 'parent');
2545
+ let parentSliding;
2546
+ let parentHiding;
2547
+ if (parentState.exist) {
2548
+ parentSliding = !!(parentEl && parentEl.classList && parentEl.classList.contains('sliding'));
2549
+ parentHiding = !!(parentEl && parentEl.classList && parentEl.classList.contains('slide-down'));
2550
+ if (parentSliding && !parentHiding) { return; }
2551
+ // hide the ancestor nodes and sibling nodes of the specified node
2552
+ if (parentState.visible && !parentHiding) {
2553
+ this.hideParent(nodeElement);
2554
+ if (parentEl) {
2555
+ parentEl.addEventListener('transitionend', function (transitionEvent) {
2556
+ transitionEvent.topEdge = topEdgeElement;
2557
+ orgChart.HideFirstParentEnd(transitionEvent);
2558
+ }, { once: true });
2559
+ }
2560
+ this.triggerHideEvent(nodeElement, 'parent');
2561
+ } else { // show the ancestors and siblings
2562
+ this.showParent(nodeElement);
2563
+ this.triggerShowEvent(nodeElement, 'parent');
2564
+ }
2565
+ }
2566
+ },
2567
+ // actions on clinking bottom edge of a node
2568
+ bottomEdgeClickHandler: function (event) {
2569
+ const nativeEvent = event.originalEvent || event;
2570
+ const nodeElement = event.node || nativeEvent.currentTarget;
2571
+ const childrenState = this.getNodeState(nodeElement, 'children');
2572
+ let childNodesContainerElement;
2573
+ let childNodeElements;
2574
+ if (childrenState.exist) {
2575
+ childNodesContainerElement = Array.from(nodeElement.parentElement ? nodeElement.parentElement.children : []).find(function (childEl) {
2576
+ return childEl !== nodeElement && childEl.classList && childEl.classList.contains('nodes');
2577
+ }) || null;
2578
+ childNodeElements = childNodesContainerElement
2579
+ ? Array.from(childNodesContainerElement.children).filter(function (childEl) {
2580
+ return childEl.classList && childEl.classList.contains('hierarchy');
2581
+ }).map(function (childHierarchyEl) {
2582
+ return childHierarchyEl.querySelector('.node');
2583
+ }).filter(Boolean)
2584
+ : [];
2585
+ if (childNodeElements.some(function (childNodeEl) { return childNodeEl.classList.contains('sliding'); })) { return; }
2586
+ // hide the descendant nodes of the specified node
2587
+ if (childrenState.visible) {
2588
+ this.hideChildren(nodeElement);
2589
+ this.triggerHideEvent(nodeElement, 'children');
2590
+ } else { // show the descendants
2591
+ this.showChildren(nodeElement);
2592
+ this.triggerShowEvent(nodeElement, 'children');
2593
+ }
2594
+ }
2595
+ },
2596
+ // actions on clicking horizontal edges
2597
+ hEdgeClickHandler: function (event) {
2598
+ const nativeEvent = event.originalEvent || event;
2599
+ const nodeElement = event.node || nativeEvent.currentTarget;
2600
+ const edgeElement = getElement(event.edge || nativeEvent.target);
2601
+ const opts = this.options;
2602
+ const siblingsState = this.getNodeState(nodeElement, 'siblings');
2603
+ let hierarchyElement;
2604
+ let siblingElements;
2605
+ let previousSiblingElement;
2606
+ let nextSiblingElement;
2607
+ if (siblingsState.exist) {
2608
+ hierarchyElement = nodeElement.closest('.hierarchy');
2609
+ siblingElements = hierarchyElement && hierarchyElement.parentElement
2610
+ ? Array.from(hierarchyElement.parentElement.children).filter(function (childEl) {
2611
+ return childEl !== hierarchyElement;
2612
+ })
2613
+ : [];
2614
+ if (siblingElements.some(function (siblingEl) {
2615
+ return siblingEl.querySelector('.sliding');
2616
+ })) { return; }
2617
+ if (opts.toggleSiblingsResp) {
2618
+ previousSiblingElement = hierarchyElement ? hierarchyElement.previousElementSibling : null;
2619
+ nextSiblingElement = hierarchyElement ? hierarchyElement.nextElementSibling : null;
2620
+ if (edgeElement && edgeElement.classList && edgeElement.classList.contains('leftEdge')) {
2621
+ if (previousSiblingElement && previousSiblingElement.classList.contains('hidden')) {
2622
+ this.showSiblings(nodeElement, 'left');
2623
+ this.triggerShowEvent(nodeElement,'siblings');
2624
+ } else {
2625
+ this.hideSiblings(nodeElement, 'left');
2626
+ this.triggerHideEvent(nodeElement, 'siblings');
2627
+ }
2628
+ } else {
2629
+ if (nextSiblingElement && nextSiblingElement.classList.contains('hidden')) {
2630
+ this.showSiblings(nodeElement, 'right');
2631
+ this.triggerShowEvent(nodeElement,'siblings');
2632
+ } else {
2633
+ this.hideSiblings(nodeElement, 'right');
2634
+ this.triggerHideEvent(nodeElement, 'siblings');
2635
+ }
2636
+ }
2637
+ } else {
2638
+ if (siblingsState.visible) {
2639
+ this.hideSiblings(nodeElement);
2640
+ this.triggerHideEvent(nodeElement, 'siblings');
2641
+ } else {
2642
+ this.showSiblings(nodeElement);
2643
+ this.triggerShowEvent(nodeElement, 'siblings');
2644
+ }
2645
+ }
2646
+ }
2647
+ },
2648
+ // show the compact node's children in the compact mode
2649
+ backToCompactHandler: function (event) {
2650
+ const nativeEvent = event.originalEvent || event;
2651
+ const compactNodeEl = event.compactNode || nativeEvent.currentTarget;
2652
+ let nestedLooseModeElements;
2653
+ let backToCompactSymbolElement;
2654
+ let backToLooseSymbolElement;
2655
+
2656
+ if (!compactNodeEl) {
2657
+ return;
2658
+ }
2659
+
2660
+ compactNodeEl.classList.remove('looseMode');
2661
+ nestedLooseModeElements = Array.from(compactNodeEl.querySelectorAll('.looseMode'));
2662
+ nestedLooseModeElements.forEach(function (nestedLooseModeEl) {
2663
+ const nestedBackToCompactSymbolElement = Array.from(nestedLooseModeEl.children || []).find(function (childEl) {
2664
+ return childEl.classList && childEl.classList.contains('backToCompactSymbol');
2665
+ }) || null;
2666
+ const nestedBackToLooseSymbolElement = Array.from(nestedLooseModeEl.children || []).find(function (childEl) {
2667
+ return childEl.classList && childEl.classList.contains('backToLooseSymbol');
2668
+ }) || null;
2669
+
2670
+ nestedLooseModeEl.classList.remove('looseMode');
2671
+ if (nestedBackToCompactSymbolElement) {
2672
+ nestedBackToCompactSymbolElement.classList.add('hidden');
2673
+ }
2674
+ if (nestedBackToLooseSymbolElement) {
2675
+ nestedBackToLooseSymbolElement.classList.remove('hidden');
2676
+ }
2677
+ });
2678
+
2679
+ backToCompactSymbolElement = Array.from(compactNodeEl.children || []).find(function (childEl) {
2680
+ return childEl.classList && childEl.classList.contains('backToCompactSymbol');
2681
+ }) || null;
2682
+ backToLooseSymbolElement = Array.from(compactNodeEl.children || []).find(function (childEl) {
2683
+ return childEl.classList && childEl.classList.contains('backToLooseSymbol');
2684
+ }) || null;
2685
+ if (backToCompactSymbolElement) {
2686
+ backToCompactSymbolElement.classList.add('hidden');
2687
+ }
2688
+ if (backToLooseSymbolElement) {
2689
+ backToLooseSymbolElement.classList.remove('hidden');
2690
+ }
2691
+ },
2692
+ // show the compact node's children in the loose mode
2693
+ backToLooseHandler: function (event) {
2694
+ const nativeEvent = event.originalEvent || event;
2695
+ const compactNodeEl = event.compactNode || nativeEvent.currentTarget;
2696
+ let backToLooseSymbolElement;
2697
+ let backToCompactSymbolElement;
2698
+
2699
+ if (!compactNodeEl) {
2700
+ return;
2701
+ }
2702
+
2703
+ compactNodeEl.classList.add('looseMode');
2704
+ backToLooseSymbolElement = Array.from(compactNodeEl.children || []).find(function (childEl) {
2705
+ return childEl.classList && childEl.classList.contains('backToLooseSymbol');
2706
+ }) || null;
2707
+ backToCompactSymbolElement = Array.from(compactNodeEl.children || []).find(function (childEl) {
2708
+ return childEl.classList && childEl.classList.contains('backToCompactSymbol');
2709
+ }) || null;
2710
+ if (backToLooseSymbolElement) {
2711
+ backToLooseSymbolElement.classList.add('hidden');
2712
+ }
2713
+ if (backToCompactSymbolElement) {
2714
+ backToCompactSymbolElement.classList.remove('hidden');
2715
+ }
2716
+ },
2717
+ //
2718
+ expandVNodesEnd: function (event) {
2719
+ const nativeEvent = event.originalEvent || event;
2720
+ const verticalNodeElements = event.vNodes || nativeEvent.vNodes;
2721
+
2722
+ getElements(verticalNodeElements).forEach(function (verticalNodeEl) {
2723
+ if (verticalNodeEl.classList) {
2724
+ verticalNodeEl.classList.remove('sliding');
2725
+ }
2726
+ });
2727
+ },
2728
+ //
2729
+ collapseVNodesEnd: function (event) {
2730
+ const nativeEvent = event.originalEvent || event;
2731
+ const verticalNodeElements = event.vNodes || nativeEvent.vNodes;
2732
+
2733
+ getElements(verticalNodeElements).forEach(function (verticalNodeEl) {
2734
+ const verticalNodesWrapperElement = verticalNodeEl && typeof verticalNodeEl.closest === 'function'
2735
+ ? verticalNodeEl.closest('ul')
2736
+ : null;
2737
+
2738
+ if (verticalNodeEl.classList) {
2739
+ verticalNodeEl.classList.remove('sliding');
2740
+ }
2741
+ if (verticalNodesWrapperElement && verticalNodesWrapperElement.classList) {
2742
+ verticalNodesWrapperElement.classList.add('hidden');
2743
+ }
2744
+ });
2745
+ },
2746
+ // event handler for toggle buttons in Hybrid(horizontal + vertical) OrgChart
2747
+ toggleVNodes: function (event) {
2748
+ const orgChart = this;
2749
+ const nativeEvent = event.originalEvent || event;
2750
+ const toggleBtnEl = event.toggleButton || nativeEvent.target;
2751
+ const descendantWrapperElement = toggleBtnEl && toggleBtnEl.parentElement ? toggleBtnEl.parentElement.nextElementSibling : null;
2752
+ let descendantNodeElements;
2753
+ let childNodeElements;
2754
+
2755
+ if (!toggleBtnEl || !descendantWrapperElement) {
2756
+ return;
2757
+ }
2758
+
2759
+ descendantNodeElements = Array.from(descendantWrapperElement.querySelectorAll('.node'));
2760
+ childNodeElements = Array.from(descendantWrapperElement.children || []).map(function (childEl) {
2761
+ return childEl.querySelector('.node');
2762
+ }).filter(Boolean);
2763
+ if (childNodeElements.some(function (childNodeEl) { return childNodeEl.classList.contains('sliding'); })) { return; }
2764
+ toggleBtnEl.classList.toggle(this.options.icons.collapsed);
2765
+ toggleBtnEl.classList.toggle(this.options.icons.expanded);
2766
+ if (descendantNodeElements[0] && descendantNodeElements[0].classList.contains('slide-up')) {
2767
+ descendantWrapperElement.classList.remove('hidden');
2768
+ this.repaint(childNodeElements[0]);
2769
+ childNodeElements.forEach(function (childNodeEl) {
2770
+ childNodeEl.classList.add('sliding');
2771
+ childNodeEl.classList.remove('slide-up');
2772
+ });
2773
+ if (childNodeElements[0]) {
2774
+ childNodeElements[0].addEventListener('transitionend', function (transitionEvent) {
2775
+ transitionEvent.vNodes = childNodeElements;
2776
+ orgChart.expandVNodesEnd(transitionEvent);
2777
+ }, { once: true });
2778
+ }
2779
+ } else {
2780
+ descendantNodeElements.forEach(function (descendantNodeEl) {
2781
+ descendantNodeEl.classList.add('sliding', 'slide-up');
2782
+ });
2783
+ if (descendantNodeElements[0]) {
2784
+ descendantNodeElements[0].addEventListener('transitionend', function (transitionEvent) {
2785
+ transitionEvent.vNodes = descendantNodeElements;
2786
+ orgChart.collapseVNodesEnd(transitionEvent);
2787
+ }, { once: true });
2788
+ }
2789
+ descendantNodeElements.forEach(function (descendantNodeEl) {
2790
+ Array.from(descendantNodeEl.querySelectorAll('.toggleBtn')).forEach(function (nestedToggleBtnEl) {
2791
+ nestedToggleBtnEl.classList.remove(this.options.icons.collapsed, this.options.icons.expanded);
2792
+ }, this);
2793
+ }, this);
2794
+ }
2795
+ },
2796
+ //
2797
+ createGhostNode: function (event) {
2798
+ const nativeEvent = event.originalEvent || event;
2799
+ const sourceNodeEl = event.sourceNode || nativeEvent.target;
2800
+ const chartElement = sourceNodeEl && typeof sourceNodeEl.closest === 'function'
2801
+ ? sourceNodeEl.closest('.orgchart')
2802
+ : null;
2803
+ const opts = this.options;
2804
+ const origEvent = nativeEvent;
2805
+ const isFirefox = /firefox/.test(getWindow().navigator.userAgent.toLowerCase());
2806
+ let sourceRect;
2807
+ let transformValue;
2808
+ let transformValues;
2809
+ let isVerticalChartDirection;
2810
+ let scaleFactor;
2811
+ let ghostNodeEl, nodeCoverEl;
2812
+
2813
+ if (!sourceNodeEl || !chartElement) {
2814
+ return;
2815
+ }
2816
+
2817
+ if (!getDocument().querySelector('.ghost-node')) {
2818
+ ghostNodeEl = getDocument().createElementNS("http://www.w3.org/2000/svg", "svg");
2819
+ if (!ghostNodeEl.classList) return;
2820
+ ghostNodeEl.classList.add('ghost-node');
2821
+ nodeCoverEl = getDocument().createElementNS('http://www.w3.org/2000/svg','rect');
2822
+ ghostNodeEl.appendChild(nodeCoverEl);
2823
+ chartElement.appendChild(ghostNodeEl);
2824
+ } else {
2825
+ ghostNodeEl = chartElement.querySelector('.ghost-node');
2826
+ nodeCoverEl = ghostNodeEl ? ghostNodeEl.firstElementChild : null;
2827
+ }
2828
+
2829
+ if (!ghostNodeEl || !nodeCoverEl) {
2830
+ return;
2831
+ }
2832
+
2833
+ sourceRect = sourceNodeEl.getBoundingClientRect();
2834
+ transformValue = chartElement.style.transform || getWindow().getComputedStyle(chartElement).transform || 'none';
2835
+ transformValues = transformValue.split(',');
2836
+ isVerticalChartDirection = opts.direction === 't2b' || opts.direction === 'b2t';
2837
+ scaleFactor = Math.abs(getWindow().parseFloat(isVerticalChartDirection ? transformValues[0].slice(transformValues[0].indexOf('(') + 1) : transformValues[1]));
2838
+ ghostNodeEl.setAttribute('width', isVerticalChartDirection ? sourceRect.width : sourceRect.height);
2839
+ ghostNodeEl.setAttribute('height', isVerticalChartDirection ? sourceRect.height : sourceRect.width);
2840
+ nodeCoverEl.setAttribute('x',5 * scaleFactor);
2841
+ nodeCoverEl.setAttribute('y',5 * scaleFactor);
2842
+ nodeCoverEl.setAttribute('width', 120 * scaleFactor);
2843
+ nodeCoverEl.setAttribute('height', 40 * scaleFactor);
2844
+ nodeCoverEl.setAttribute('rx', 4 * scaleFactor);
2845
+ nodeCoverEl.setAttribute('ry', 4 * scaleFactor);
2846
+ nodeCoverEl.setAttribute('stroke-width', 1 * scaleFactor);
2847
+ let xOffset = origEvent.offsetX * scaleFactor;
2848
+ let yOffset = origEvent.offsetY * scaleFactor;
2849
+ if (opts.direction === 'l2r') {
2850
+ xOffset = origEvent.offsetY * scaleFactor;
2851
+ yOffset = origEvent.offsetX * scaleFactor;
2852
+ } else if (opts.direction === 'r2l') {
2853
+ xOffset = sourceRect.width - origEvent.offsetY * scaleFactor;
2854
+ yOffset = origEvent.offsetX * scaleFactor;
2855
+ } else if (opts.direction === 'b2t') {
2856
+ xOffset = sourceRect.width - origEvent.offsetX * scaleFactor;
2857
+ yOffset = sourceRect.height - origEvent.offsetY * scaleFactor;
2858
+ }
2859
+ if (isFirefox) { // hack for old version of Firefox(< 48.0)
2860
+ nodeCoverEl.setAttribute('fill', 'rgb(255, 255, 255)');
2861
+ nodeCoverEl.setAttribute('stroke', 'rgb(191, 0, 0)');
2862
+ const ghostNodeImageEl = getDocument().createElement('img');
2863
+ ghostNodeImageEl.src = 'data:image/svg+xml;utf8,' + (new XMLSerializer()).serializeToString(ghostNodeEl);
2864
+ origEvent.dataTransfer.setDragImage(ghostNodeImageEl, xOffset, yOffset);
2865
+ } else {
2866
+ // IE/Edge do not support this, so only use it if we can
2867
+ if (origEvent.dataTransfer.setDragImage)
2868
+ origEvent.dataTransfer.setDragImage(ghostNodeEl, xOffset, yOffset);
2869
+ }
2870
+ },
2871
+ // get the level amount of a hierachy
2872
+ getUpperLevel: function (nodeEl) {
2873
+ const nodeElement = getElement(nodeEl);
2874
+ let currentElement;
2875
+ let levelCount = 0;
2876
+
2877
+ if (!nodeElement || !nodeElement.matches('.node')) {
2878
+ return 0;
2879
+ }
2880
+
2881
+ currentElement = nodeElement.parentElement;
2882
+ while (currentElement) {
2883
+ if (currentElement.classList && currentElement.classList.contains('hierarchy')) {
2884
+ levelCount += 1;
2885
+ }
2886
+ currentElement = currentElement.parentElement;
2887
+ }
2888
+
2889
+ return levelCount;
2890
+ },
2891
+ // get the level amount of a hierachy
2892
+ getLowerLevel: function (nodeEl) {
2893
+ const nodeElement = getElement(nodeEl);
2894
+ let levelCount = 1;
2895
+ let currentLevelNodes;
2896
+
2897
+ if (!nodeElement || !nodeElement.matches('.node')) {
2898
+ return 0;
2899
+ }
2900
+
2901
+ currentLevelNodes = this.getChildren(nodeElement);
2902
+ while (currentLevelNodes.length) {
2903
+ const nextLevelNodes = [];
2904
+ levelCount += 1;
2905
+ currentLevelNodes.forEach(function (childNodeEl) {
2906
+ Array.prototype.push.apply(nextLevelNodes, this.getChildren(childNodeEl));
2907
+ }, this);
2908
+ currentLevelNodes = nextLevelNodes;
2909
+ }
2910
+
2911
+ return levelCount;
2912
+ },
2913
+ // get nodes in level order traversal
2914
+ getLevelOrderNodes: function (rootEl) {
2915
+ const rootElement = getElement(rootEl);
2916
+ const pendingNodeElements = [];
2917
+ const nodesByLevel = [];
2918
+ if (!rootElement) {
2919
+ return [];
2920
+ }
2921
+
2922
+ pendingNodeElements.push(rootElement);
2923
+ while(pendingNodeElements.length) {
2924
+ const levelNodeElements = [];
2925
+ const levelNodeCount = pendingNodeElements.length;
2926
+ for(let i = 0; i < levelNodeCount; i++) {
2927
+ const currentNodeElement = pendingNodeElements.shift();
2928
+ const childNodeElements = this.getChildren(currentNodeElement);
2929
+ if(childNodeElements.length) {
2930
+ Array.prototype.push.apply(pendingNodeElements, childNodeElements);
2931
+ }
2932
+ levelNodeElements.push(currentNodeElement);
2933
+ }
2934
+ nodesByLevel.push(levelNodeElements);
2935
+ }
2936
+ return nodesByLevel;
2937
+ },
2938
+ //
2939
+ filterAllowedDropNodes: function (draggedEl) {
2940
+ const draggedNodeEl = getElement(draggedEl);
2941
+ const opts = this.options;
2942
+ const chartElement = this.chart;
2943
+ let draggableHostEl;
2944
+ let draggedParentNodeEl;
2945
+ let draggedSubtreeNodeEls;
2946
+ let excludedNodeEls;
2947
+ let draggingNode;
2948
+
2949
+ if (!chartElement) {
2950
+ return;
2951
+ }
2952
+
2953
+ // what is being dragged? a node, or something within a node?
2954
+ draggableHostEl = draggedNodeEl && typeof draggedNodeEl.closest === 'function'
2955
+ ? draggedNodeEl.closest('[draggable]')
2956
+ : null;
2957
+ draggingNode = !!(draggableHostEl && draggableHostEl.classList && draggableHostEl.classList.contains('node'));
2958
+ draggedParentNodeEl = draggedNodeEl && draggedNodeEl.closest('.nodes')
2959
+ ? draggedNodeEl.closest('.nodes').previousElementSibling
2960
+ : null;
2961
+ draggedSubtreeNodeEls = draggedNodeEl && draggedNodeEl.closest('.hierarchy')
2962
+ ? Array.from(draggedNodeEl.closest('.hierarchy').querySelectorAll('.node'))
2963
+ : [];
2964
+ excludedNodeEls = new Set(draggedSubtreeNodeEls);
2965
+ setState(chartElement, 'dragged', draggedNodeEl);
2966
+ Array.from(chartElement.querySelectorAll('.node')).forEach(function (node) {
2967
+ if (!draggingNode || !excludedNodeEls.has(node)) {
2968
+ if (opts.dropCriteria) {
2969
+ if (opts.dropCriteria(draggedNodeEl, draggedParentNodeEl, node)) {
2970
+ node.classList.add('allowedDrop');
2971
+ }
2972
+ } else {
2973
+ node.classList.add('allowedDrop');
2974
+ }
2975
+ }
2976
+ });
2977
+ },
2978
+ //
2979
+ dragstartHandler: function (event) {
2980
+ const nativeEvent = event.originalEvent || event;
2981
+ const draggedNodeEl = event.draggedNode || nativeEvent.target;
2982
+ const chartElement = this.chart;
2983
+ const transformValue = chartElement
2984
+ ? (chartElement.style.transform || getWindow().getComputedStyle(chartElement).transform || 'none')
2985
+ : 'none';
2986
+
2987
+ nativeEvent.dataTransfer.setData('text/html', 'hack for firefox');
2988
+ // if users enable zoom or direction options
2989
+ if (transformValue !== 'none') {
2990
+ this.createGhostNode({
2991
+ sourceNode: draggedNodeEl,
2992
+ originalEvent: nativeEvent
2993
+ });
2994
+ }
2995
+ this.filterAllowedDropNodes(draggedNodeEl);
2996
+ },
2997
+ //
2998
+ dragoverHandler: function (event) {
2999
+ const nativeEvent = event.originalEvent || event;
3000
+ const dropZoneEl = event.dropZone || nativeEvent.currentTarget;
3001
+
3002
+ if (!dropZoneEl || !dropZoneEl.classList || !dropZoneEl.classList.contains('allowedDrop')) {
3003
+ nativeEvent.dataTransfer.dropEffect = 'none';
3004
+ } else {
3005
+ // default action for drag-and-drop of div is not to drop, so preventing default action for nodes which have allowedDrop class
3006
+ //to fix drag and drop on IE and Edge
3007
+ if (typeof nativeEvent.preventDefault === 'function') {
3008
+ nativeEvent.preventDefault();
3009
+ }
3010
+ }
3011
+ },
3012
+ //
3013
+ dragendHandler: function (event) {
3014
+ const chartElement = this.chart;
3015
+
3016
+ if (!chartElement) {
3017
+ return;
3018
+ }
3019
+
3020
+ Array.from(chartElement.querySelectorAll('.allowedDrop')).forEach(function (node) {
3021
+ node.classList.remove('allowedDrop');
3022
+ });
3023
+ },
3024
+ // when user drops the node, it will be removed from original parent node and be added to new parent node
3025
+ dropHandler: async function (event) {
3026
+ const nativeEvent = event.originalEvent || event;
3027
+ const orgChart = this;
3028
+ const chartElement = this.chart;
3029
+ const dropZoneEl = event.dropZone || nativeEvent.currentTarget;
3030
+ const draggedEl = chartElement ? getElement(getState(chartElement, 'dragged')) : null;
3031
+ const chartOptions = chartElement ? getState(chartElement, 'options') : null;
3032
+ let draggedParentNodeEl;
3033
+ let dropChildrenContainerEl;
3034
+ let draggedHierarchyEl;
3035
+ let draggedParentChildrenContainerEl;
3036
+ let remainingHierarchyEls;
3037
+
3038
+ // Pass on drops which are not nodes (since they are not our doing)
3039
+ if (!draggedEl || !draggedEl.classList || !draggedEl.classList.contains('node')) {
3040
+ if (chartElement) {
3041
+ chartElement.dispatchEvent(createTriggeredEvent('otherdropped.orgchart', {
3042
+ 'draggedItem': draggedEl,
3043
+ 'dropZone': dropZoneEl
3044
+ }));
3045
+ }
3046
+ return;
3047
+ }
3048
+
3049
+ if (!dropZoneEl || !dropZoneEl.classList || !dropZoneEl.classList.contains('allowedDrop')) {
3050
+ // We are trying to drop a node into a node which isn't allowed
3051
+ // IE/Edge have a habit of allowing this, so we need our own double-check
3052
+ return;
3053
+ }
3054
+
3055
+ draggedParentNodeEl = draggedEl.closest('.nodes') ? draggedEl.closest('.nodes').previousElementSibling : null;
3056
+ const dropEvent = createEventLike('nodedrop.orgchart');
3057
+ if (chartElement) {
3058
+ const triggeredDropEvent = createTriggeredEvent(dropEvent, {
3059
+ 'draggedNode': draggedEl,
3060
+ 'dragZone': draggedParentNodeEl,
3061
+ 'dropZone': dropZoneEl
3062
+ });
3063
+
3064
+ triggeredDropEvent.delegateTarget = chartElement;
3065
+ chartElement.dispatchEvent(triggeredDropEvent);
3066
+ dropEvent.defaultPrevented = triggeredDropEvent.defaultPrevented;
3067
+ }
3068
+ if (dropEvent.isDefaultPrevented()) {
3069
+ return;
3070
+ }
3071
+ // special process for hybrid chart
3072
+ const datasource = chartOptions.data;
3073
+ const dataDigger = new JSONDigger(datasource, chartOptions.nodeId, 'children');
3074
+ const hybridNodeData = dataDigger.findOneNode({ 'hybrid': true });
3075
+ if (chartOptions.verticalLevel > 1 || hybridNodeData) {
3076
+ const draggedNodeData = dataDigger.findNodeById(getState(draggedEl, 'nodeData').id);
3077
+ const draggedNodeCopy = Object.assign({}, draggedNodeData);
3078
+ dataDigger.removeNode(draggedNodeData.id);
3079
+ const dropNodeData = dataDigger.findNodeById(getState(dropZoneEl, 'nodeData').id);
3080
+ if (dropNodeData.children) {
3081
+ dropNodeData.children.push(draggedNodeCopy);
3082
+ } else {
3083
+ dropNodeData.children = [draggedNodeCopy];
3084
+ }
3085
+ orgChart.attachRel(datasource, '00');
3086
+ orgChart.init({ 'data': datasource });
3087
+ } else {
3088
+ // The folowing code snippets are used to process horizontal chart
3089
+ draggedHierarchyEl = draggedEl.closest('.hierarchy');
3090
+ dropChildrenContainerEl = Array.from(dropZoneEl.parentElement ? dropZoneEl.parentElement.children : []).find(function (childEl) {
3091
+ return childEl !== dropZoneEl && childEl.classList && childEl.classList.contains('nodes');
3092
+ }) || null;
3093
+ // firstly, deal with the hierarchy of drop zone
3094
+ if (!dropChildrenContainerEl) { // if the drop zone is a leaf node
3095
+ dropZoneEl.insertAdjacentHTML('beforeend', `<i class="edge verticalEdge bottomEdge ${this.options.icons.theme}"></i>`);
3096
+ dropZoneEl.insertAdjacentHTML('afterend', '<ul class="nodes"></ul>');
3097
+ dropChildrenContainerEl = dropZoneEl.nextElementSibling;
3098
+ Array.from(draggedEl.querySelectorAll('.horizontalEdge')).forEach(function (edgeEl) {
3099
+ edgeEl.remove();
3100
+ });
3101
+ dropChildrenContainerEl.appendChild(draggedHierarchyEl);
3102
+ const titleEl = Array.from(dropZoneEl.children || []).find(function (childEl) {
3103
+ return childEl.classList && childEl.classList.contains('title');
3104
+ }) || null;
3105
+ if (titleEl) {
3106
+ titleEl.insertAdjacentHTML('afterbegin', `<i class="${this.options.icons.theme} ${chartOptions.icons.parentNode} parentNodeSymbol"></i>`);
3107
+ }
3108
+ } else {
3109
+ const horizontalEdges = `<i class="edge horizontalEdge rightEdge ${this.options.icons.theme}"></i><i class="edge horizontalEdge leftEdge ${this.options.icons.theme}"></i>`;
3110
+ if (!draggedEl.querySelector('.horizontalEdge')) {
3111
+ draggedEl.insertAdjacentHTML('beforeend', horizontalEdges);
3112
+ }
3113
+ dropChildrenContainerEl.appendChild(draggedHierarchyEl);
3114
+ const dropSiblingNodeEls = Array.from(dropChildrenContainerEl.children || []).filter(function (childEl) {
3115
+ return childEl !== draggedHierarchyEl && childEl.classList && childEl.classList.contains('hierarchy');
3116
+ }).map(function (siblingHierarchyEl) {
3117
+ return siblingHierarchyEl.querySelector('.node');
3118
+ }).filter(Boolean);
3119
+ if (dropSiblingNodeEls.length === 1) {
3120
+ dropSiblingNodeEls[0].insertAdjacentHTML('beforeend', horizontalEdges);
3121
+ }
3122
+ }
3123
+ // secondly, deal with the hierarchy of dragged node
3124
+ draggedParentChildrenContainerEl = Array.from(draggedParentNodeEl && draggedParentNodeEl.parentElement ? draggedParentNodeEl.parentElement.children : []).find(function (childEl) {
3125
+ return childEl !== draggedParentNodeEl && childEl.classList && childEl.classList.contains('nodes');
3126
+ }) || null;
3127
+ remainingHierarchyEls = draggedParentChildrenContainerEl
3128
+ ? Array.from(draggedParentChildrenContainerEl.children || []).filter(function (childEl) {
3129
+ return childEl.classList && childEl.classList.contains('hierarchy');
3130
+ })
3131
+ : [];
3132
+ if (remainingHierarchyEls.length === 1) { // if there is only one sibling node left
3133
+ const remainingNodeEl = remainingHierarchyEls[0].querySelector('.node');
3134
+ if (remainingNodeEl) {
3135
+ Array.from(remainingNodeEl.querySelectorAll('.horizontalEdge')).forEach(function (edgeEl) {
3136
+ edgeEl.remove();
3137
+ });
3138
+ }
3139
+ } else if (remainingHierarchyEls.length === 0) {
3140
+ Array.from(draggedParentNodeEl.querySelectorAll('.bottomEdge, .parentNodeSymbol')).forEach(function (edgeEl) {
3141
+ edgeEl.remove();
3142
+ });
3143
+ if (draggedParentChildrenContainerEl) {
3144
+ draggedParentChildrenContainerEl.remove();
3145
+ }
3146
+ }
3147
+ }
3148
+ },
3149
+ //
3150
+ touchstartHandler: function (event) {
3151
+ const nativeEvent = event.originalEvent || event;
3152
+
3153
+ if (this.touchHandled)
3154
+ return;
3155
+
3156
+ if (nativeEvent.touches && nativeEvent.touches.length > 1)
3157
+ return;
3158
+
3159
+ this.touchHandled = true;
3160
+ this.touchMoved = false; // this is so we can work out later if this was a 'press' or a 'drag' touch
3161
+ if (typeof nativeEvent.preventDefault === 'function') {
3162
+ nativeEvent.preventDefault();
3163
+ }
3164
+ },
3165
+ //
3166
+ touchmoveHandler: function (event) {
3167
+ const nativeEvent = event.originalEvent || event;
3168
+ const draggedNodeEl = event.draggedNode || nativeEvent.currentTarget || nativeEvent.target;
3169
+ const chartElement = this.chart;
3170
+ let cachedDraggedNodeEl;
3171
+ let touchedElement;
3172
+ let candidateDropNodeEl;
3173
+
3174
+ if (!this.touchHandled)
3175
+ return;
3176
+
3177
+ if (nativeEvent.touches && nativeEvent.touches.length > 1)
3178
+ return;
3179
+
3180
+ if (typeof nativeEvent.preventDefault === 'function') {
3181
+ nativeEvent.preventDefault();
3182
+ }
3183
+
3184
+ if (!this.touchMoved) {
3185
+ // we do not bother with createGhostNode (dragstart does) since the touch event does not have a dataTransfer property
3186
+ this.filterAllowedDropNodes(draggedNodeEl); // will also cache the dragged node on the chart element for us
3187
+ // create an image which can be used to illustrate the drag (our own createGhostNode)
3188
+ cachedDraggedNodeEl = chartElement ? getElement(getState(chartElement, 'dragged')) : null;
3189
+ if (!cachedDraggedNodeEl) {
3190
+ return;
3191
+ }
3192
+ this.touchDragImage = this.createDragImage(nativeEvent, cachedDraggedNodeEl);
3193
+ }
3194
+ this.touchMoved = true;
3195
+
3196
+ // move our dragimage so it follows our finger
3197
+ this.moveDragImage(nativeEvent, this.touchDragImage);
3198
+
3199
+ touchedElement = getDocument().elementFromPoint(nativeEvent.touches[0].clientX, nativeEvent.touches[0].clientY);
3200
+ candidateDropNodeEl = touchedElement && typeof touchedElement.closest === 'function'
3201
+ ? touchedElement.closest('div.node')
3202
+ : null;
3203
+ if (candidateDropNodeEl && candidateDropNodeEl.classList && candidateDropNodeEl.classList.contains('allowedDrop')) {
3204
+ this.touchTargetNode = candidateDropNodeEl;
3205
+ } else {
3206
+ this.touchTargetNode = null;
3207
+ }
3208
+ },
3209
+ //
3210
+ touchendHandler: function (event) {
3211
+ const nativeEvent = event.originalEvent || event;
3212
+ let firstTouch;
3213
+ let targetEl;
3214
+ let fakeMouseClickEvent;
3215
+
3216
+ if (!this.touchHandled) {
3217
+ return;
3218
+ }
3219
+
3220
+ this.destroyDragImage();
3221
+ if (this.touchMoved) {
3222
+ // we've had movement, so this was a 'drag' touch
3223
+ if (this.touchTargetNode) {
3224
+ const syntheticDropEvent = { dropZone: this.touchTargetNode };
3225
+ this.dropHandler(syntheticDropEvent);
3226
+ this.touchTargetNode = null;
3227
+ }
3228
+ this.dragendHandler(event);
3229
+ } else {
3230
+ // we did not move, so this was a 'press' touch (fake a click)
3231
+ firstTouch = nativeEvent.changedTouches[0];
3232
+ targetEl = nativeEvent.target;
3233
+ fakeMouseClickEvent = new (getWindow().MouseEvent)('click', {
3234
+ bubbles: true,
3235
+ cancelable: true,
3236
+ view: getWindow(),
3237
+ screenX: firstTouch.screenX,
3238
+ screenY: firstTouch.screenY,
3239
+ clientX: firstTouch.clientX,
3240
+ clientY: firstTouch.clientY,
3241
+ ctrlKey: nativeEvent.ctrlKey,
3242
+ altKey: nativeEvent.altKey,
3243
+ shiftKey: nativeEvent.shiftKey,
3244
+ metaKey: nativeEvent.metaKey,
3245
+ button: 0
3246
+ });
3247
+ targetEl.dispatchEvent(fakeMouseClickEvent);
3248
+ }
3249
+
3250
+ this.touchHandled = false;
3251
+ },
3252
+ //
3253
+ createDragImage: function (event, sourceEl) {
3254
+ const dragImageEl = sourceEl.cloneNode(true);
3255
+ this.copyStyle(sourceEl, dragImageEl);
3256
+ dragImageEl.style.top = dragImageEl.style.left = '-9999px';
3257
+ const sourceRect = sourceEl.getBoundingClientRect();
3258
+ const sourcePoint = this.getTouchPoint(event);
3259
+ this.touchDragImageOffset = { x: sourcePoint.x - sourceRect.left, y: sourcePoint.y - sourceRect.top };
3260
+ dragImageEl.style.opacity = '0.5';
3261
+ getDocument().body.appendChild(dragImageEl);
3262
+ return dragImageEl;
3263
+ },
3264
+ //
3265
+ destroyDragImage: function () {
3266
+ if (this.touchDragImage && this.touchDragImage.parentElement)
3267
+ this.touchDragImage.parentElement.removeChild(this.touchDragImage);
3268
+ this.touchDragImageOffset = null;
3269
+ this.touchDragImage = null;
3270
+ },
3271
+ //
3272
+ copyStyle: function (sourceEl, targetEl) {
3273
+ // remove potentially troublesome attributes
3274
+ const badAttributes = ['id', 'class', 'style', 'draggable'];
3275
+ badAttributes.forEach(function (attributeName) {
3276
+ targetEl.removeAttribute(attributeName);
3277
+ });
3278
+ // copy canvas content
3279
+ if (sourceEl instanceof HTMLCanvasElement) {
3280
+ const sourceCanvas = sourceEl;
3281
+ const targetCanvas = targetEl;
3282
+ targetCanvas.width = sourceCanvas.width;
3283
+ targetCanvas.height = sourceCanvas.height;
3284
+ targetCanvas.getContext('2d').drawImage(sourceCanvas, 0, 0);
3285
+ }
3286
+ // copy style (without transitions)
3287
+ const computedStyle = getComputedStyle(sourceEl);
3288
+ for (let i = 0; i < computedStyle.length; i++) {
3289
+ const key = computedStyle[i];
3290
+ if (key.indexOf('transition') < 0) {
3291
+ targetEl.style[key] = computedStyle[key];
3292
+ }
3293
+ }
3294
+ targetEl.style.pointerEvents = 'none';
3295
+ // and repeat for all children
3296
+ for (let i = 0; i < sourceEl.children.length; i++) {
3297
+ this.copyStyle(sourceEl.children[i], targetEl.children[i]);
3298
+ }
3299
+ },
3300
+ //
3301
+ getTouchPoint: function (event) {
3302
+ let nativeEvent = event ? (event.originalEvent || event) : null;
3303
+
3304
+ if (nativeEvent && nativeEvent.touches) {
3305
+ nativeEvent = nativeEvent.touches[0];
3306
+ }
3307
+ return {
3308
+ x: nativeEvent.clientX,
3309
+ y: nativeEvent.clientY
3310
+ };
3311
+ },
3312
+ //
3313
+ moveDragImage: function (event, dragImageEl) {
3314
+ if (!event || !dragImageEl)
3315
+ return;
3316
+ const orgChartMaster = this;
3317
+ requestAnimationFrame(function () {
3318
+ const touchPoint = orgChartMaster.getTouchPoint(event);
3319
+ const dragImageStyle = dragImageEl.style;
3320
+ dragImageStyle.position = 'absolute';
3321
+ dragImageStyle.pointerEvents = 'none';
3322
+ dragImageStyle.zIndex = '999999';
3323
+ if (orgChartMaster.touchDragImageOffset) {
3324
+ dragImageStyle.left = Math.round(touchPoint.x - orgChartMaster.touchDragImageOffset.x) + 'px';
3325
+ dragImageStyle.top = Math.round(touchPoint.y - orgChartMaster.touchDragImageOffset.y) + 'px';
3326
+ }
3327
+ });
3328
+ },
3329
+ //
3330
+ bindDragDrop: function (nodeEl) {
3331
+ const nodeElement = getElement(nodeEl);
3332
+
3333
+ if (!nodeElement) {
3334
+ return;
3335
+ }
3336
+
3337
+ nodeElement.addEventListener('dragstart', this.dragstartHandler.bind(this));
3338
+ nodeElement.addEventListener('dragover', this.dragoverHandler.bind(this));
3339
+ nodeElement.addEventListener('dragend', this.dragendHandler.bind(this));
3340
+ nodeElement.addEventListener('drop', this.dropHandler.bind(this));
3341
+ nodeElement.addEventListener('touchstart', this.touchstartHandler.bind(this));
3342
+ nodeElement.addEventListener('touchmove', this.touchmoveHandler.bind(this));
3343
+ nodeElement.addEventListener('touchend', this.touchendHandler.bind(this));
3344
+ },
3345
+ // create node
3346
+ createNode: function (data) {
3347
+ const opts = this.options;
3348
+ const level = data.level;
3349
+ const flags = data.relationship || '';
3350
+ const nodeClassName = [
3351
+ 'node',
3352
+ data.className || '',
3353
+ data?.outsider ? 'outsider' : '',
3354
+ level > opts.visibleLevel ? 'slide-up' : ''
3355
+ ].filter(Boolean).join(' ');
3356
+ const titleMarkupParts = [];
3357
+ const contentMarkupParts = [];
3358
+ const nodeMarkupParts = [];
3359
+ const shouldAddParentSymbol = Number(flags.substr(2, 1));
3360
+
3361
+ if (data.children && data[opts.nodeId]) {
3362
+ forEachValue(data.children, function (index, child) {
3363
+ child.parentId = data[opts.nodeId]
3364
+ });
3365
+ }
3366
+
3367
+ if (shouldAddParentSymbol) {
3368
+ titleMarkupParts.push(`<i class="${opts.icons.theme} ${opts.icons.parentNode} parentNodeSymbol"></i>`);
3369
+ }
3370
+ titleMarkupParts.push(data[opts.nodeTitle]);
3371
+
3372
+ if (opts.nodeTemplate) {
3373
+ nodeMarkupParts.push(opts.nodeTemplate(data));
3374
+ } else {
3375
+ nodeMarkupParts.push(`<div class="title">${titleMarkupParts.join('')}</div>`);
3376
+ if (typeof opts.nodeContent !== 'undefined') {
3377
+ contentMarkupParts.push(`<div class="content">${data[opts.nodeContent] || ''}</div>`);
3378
+ }
3379
+ nodeMarkupParts.push(contentMarkupParts.join(''));
3380
+ }
3381
+
3382
+ if ((opts.verticalLevel && level >= opts.verticalLevel) || data.vertical) {
3383
+ if (shouldAddParentSymbol) {
3384
+ nodeMarkupParts.push(`<i class="toggleBtn ${opts.icons.theme}"></i>`);
3385
+ }
3386
+ } else if (data.hybrid) {
3387
+ if (shouldAddParentSymbol) {
3388
+ nodeMarkupParts.push(`<i class="edge verticalEdge bottomEdge ${opts.icons.theme}"></i>`);
3389
+ }
3390
+ } else if (data.compact) {
3391
+ if (shouldAddParentSymbol) {
3392
+ nodeMarkupParts.push(`<i class="${opts.icons.theme} ${opts.icons.backToCompact} backToCompactSymbol hidden"></i>`);
3393
+ nodeMarkupParts.push(`<i class="${opts.icons.theme} ${opts.icons.backToLoose} backToLooseSymbol"></i>`);
3394
+ }
3395
+ } else if (!data.associatedCompact) {
3396
+ if (Number(flags.substr(0, 1))) {
3397
+ nodeMarkupParts.push(`<i class="edge verticalEdge topEdge ${opts.icons.theme}"></i>`);
3398
+ }
3399
+ if (Number(flags.substr(1, 1))) {
3400
+ nodeMarkupParts.push(`<i class="edge horizontalEdge rightEdge ${opts.icons.theme}"></i>`);
3401
+ nodeMarkupParts.push(`<i class="edge horizontalEdge leftEdge ${opts.icons.theme}"></i>`);
3402
+ }
3403
+ if (shouldAddParentSymbol) {
3404
+ nodeMarkupParts.push(`<i class="edge verticalEdge bottomEdge ${opts.icons.theme}"></i>`);
3405
+ }
3406
+ }
3407
+
3408
+ // construct the content of node
3409
+ const nodeElement = getDocument().createElement('div');
3410
+ const routeNodeClick = function (selector, handler) {
3411
+ nodeElement.addEventListener('click', function (event) {
3412
+ const matchedElement = event.target && typeof event.target.closest === 'function'
3413
+ ? event.target.closest(selector)
3414
+ : null;
3415
+
3416
+ if (!matchedElement || matchedElement.parentElement !== nodeElement) {
3417
+ return;
3418
+ }
3419
+
3420
+ handler.call(this, event);
3421
+ }.bind(this));
3422
+ }.bind(this);
3423
+
3424
+ if (opts.draggable) {
3425
+ nodeElement.setAttribute('draggable', 'true');
3426
+ }
3427
+ if (data[opts.nodeId]) {
3428
+ nodeElement.id = data[opts.nodeId];
3429
+ }
3430
+ if (data.parentId) {
3431
+ nodeElement.setAttribute('data-parent', data.parentId);
3432
+ }
3433
+ nodeElement.className = nodeClassName;
3434
+ nodeElement.insertAdjacentHTML('beforeend', nodeMarkupParts.join(''));
3435
+ //
3436
+ const storedNodeData = mergeObjects({}, data);
3437
+ delete storedNodeData.children;
3438
+ setState(nodeElement, 'nodeData', storedNodeData);
3439
+ nodeElement.__ocNodeData = storedNodeData;
3440
+ if (data.compact && nodeElement && nodeElement.style) {
3441
+ nodeElement.style.gridTemplateColumns = `repeat(${Math.floor(Math.sqrt(data.children.length + 1))}, auto)`;
3442
+ }
3443
+
3444
+ nodeElement.addEventListener('mouseenter', this.nodeEnterLeaveHandler.bind(this));
3445
+ nodeElement.addEventListener('mouseleave', this.nodeEnterLeaveHandler.bind(this));
3446
+ nodeElement.addEventListener('click', this.nodeClickHandler.bind(this));
3447
+ routeNodeClick('.topEdge', this.topEdgeClickHandler);
3448
+ routeNodeClick('.bottomEdge', this.bottomEdgeClickHandler);
3449
+ routeNodeClick('.leftEdge, .rightEdge', this.hEdgeClickHandler);
3450
+ routeNodeClick('.toggleBtn', this.toggleVNodes);
3451
+ routeNodeClick('.backToCompactSymbol', this.backToCompactHandler);
3452
+ routeNodeClick('.backToLooseSymbol', this.backToLooseHandler);
3453
+
3454
+ if (opts.draggable) {
3455
+ this.bindDragDrop(nodeElement);
3456
+ this.touchHandled = false;
3457
+ this.touchMoved = false;
3458
+ this.touchTargetNode = null;
3459
+ }
3460
+ // allow user to append dom modification after finishing node create of orgchart
3461
+ if (opts.createNode) {
3462
+ opts.createNode(nodeElement, data);
3463
+ }
3464
+
3465
+ return nodeElement;
3466
+ },
3467
+ // Construct the inferior nodes within a hierarchy
3468
+ buildInferiorNodes: function (hierarchyEl, nodeEl, data, level) {
3469
+ const orgChart = this;
3470
+ const opts = this.options;
3471
+ const hierarchyElement = getElement(hierarchyEl);
3472
+ const nodeElement = getElement(nodeEl);
3473
+ const isHidden = level + 1 > opts.visibleLevel || (data.collapsed !== undefined && data.collapsed);
3474
+ let nodesLayerElement;
3475
+
3476
+ if (!hierarchyElement) {
3477
+ return;
3478
+ }
3479
+
3480
+ if ((opts.verticalLevel && (level + 1) >= opts.verticalLevel) || data.hybrid) {
3481
+ nodesLayerElement = getDocument().createElement('ul');
3482
+ nodesLayerElement.className = 'nodes';
3483
+ if (isHidden && (opts.verticalLevel && (level + 1) >= opts.verticalLevel)) {
3484
+ nodesLayerElement.classList.add('hidden');
3485
+ }
3486
+ if (((opts.verticalLevel && level + 1 === opts.verticalLevel) || data.hybrid)
3487
+ && !hierarchyElement.closest('.vertical')) {
3488
+ nodesLayerElement.classList.add('vertical');
3489
+ }
3490
+ hierarchyElement.appendChild(nodesLayerElement);
3491
+ } else if (data.compact) {
3492
+ if (nodeElement && nodeElement.classList) {
3493
+ nodeElement.classList.add('compact');
3494
+ }
3495
+ } else {
3496
+ nodesLayerElement = getDocument().createElement('ul');
3497
+ nodesLayerElement.className = isHidden ? 'nodes hidden' : 'nodes';
3498
+ if (isHidden) {
3499
+ hierarchyElement.classList.add('isChildrenCollapsed');
3500
+ }
3501
+ hierarchyElement.appendChild(nodesLayerElement);
3502
+ }
3503
+ // recurse through children nodes
3504
+ if (Array.isArray(data.children[0])) {
3505
+ forEachValue(data.children, function() {
3506
+ this.level = level + 1;
3507
+ });
3508
+ this.buildHierarchy(nodesLayerElement, data.children); // 构造子一层的夫妻组合(每个组合可能有多妻多夫情况)
3509
+ } else {
3510
+ forEachValue(data.children, function () {
3511
+ this.level = level + 1;
3512
+ if (data.compact) {
3513
+ orgChart.buildHierarchy(nodeElement, this);
3514
+ } else {
3515
+ const nodeCellElement = getDocument().createElement('li');
3516
+
3517
+ nodeCellElement.className = 'hierarchy';
3518
+ nodesLayerElement.appendChild(nodeCellElement);
3519
+ orgChart.buildHierarchy(nodeCellElement, this);
3520
+ }
3521
+ });
3522
+ }
3523
+ },
3524
+ // recursively build the tree
3525
+ buildHierarchy: function (hierarchyEl, data) {
3526
+ const orgChart = this;
3527
+ const hierarchyElement = getElement(hierarchyEl);
3528
+ let level = 0;
3529
+ let nodeCollection;
3530
+ let nodeElement;
3531
+
3532
+ if (!hierarchyElement) {
3533
+ return;
3534
+ }
3535
+
3536
+ if (data.level || data[0]?.level) {
3537
+ level = data.level;
3538
+ } else {
3539
+ let currentAncestorElement = hierarchyElement.parentElement;
3540
+
3541
+ while (currentAncestorElement && !currentAncestorElement.matches('.orgchart')) {
3542
+ if (currentAncestorElement.classList && currentAncestorElement.classList.contains('nodes')) {
3543
+ level += 1;
3544
+ }
3545
+ currentAncestorElement = currentAncestorElement.parentElement;
3546
+ }
3547
+ if (Array.isArray(data) && Array.isArray(data[0])) {
3548
+ forEachValue(data, function () {
3549
+ forEachValue(this, function () {
3550
+ this.level = level;
3551
+ });
3552
+ });
3553
+ } else {
3554
+ data.level = level;
3555
+ }
3556
+ }
3557
+ // Construct the single node in OrgChart or the multiple nodes in family tree
3558
+ if (Array.isArray(data) && Array.isArray(data[0])) { // 处理family tree的情况
3559
+ forEachValue(data, function () { // 构造一个家庭的hierarchy
3560
+ const familyNodes = this;
3561
+ forEachValue(this, function (i) { // 构造一个夫/妻节点
3562
+ let spouseNodeElement;
3563
+ let wrapperElement;
3564
+
3565
+ nodeElement = orgChart.createNode(this);
3566
+ // if there are only two persons in a marriage, two single nodes will appear in a hierarchy
3567
+ if (familyNodes.length === 2 && i === 1) {
3568
+ spouseNodeElement = Array.from(hierarchyElement.querySelectorAll('.node')).find(function (candidateNodeEl) {
3569
+ return candidateNodeEl.id === familyNodes[0].id;
3570
+ }) || null;
3571
+ if (spouseNodeElement && spouseNodeElement.parentElement) {
3572
+ spouseNodeElement.after(nodeElement);
3573
+ }
3574
+ if (this.children && this.children.length && this.children[0].length) {
3575
+ orgChart.buildInferiorNodes(spouseNodeElement ? spouseNodeElement.parentElement : null, nodeElement, this, level);
3576
+ }
3577
+ } else {
3578
+ // if there are more than two persons in a marriage, every node will be included in a single hierarchy
3579
+ wrapperElement = getDocument().createElement('li');
3580
+ wrapperElement.className = `hierarchy${familyNodes.length > 1 ? ' spouse' : ''}${familyNodes.length === 2 ? ' couple' : ''}${!!this.outsider === false && familyNodes.length > 2 ? ' insider' : ''}`;
3581
+
3582
+ //在family tree中,一个多妻/多夫组合里,本姓人只有一个,外姓人可能有多个,我们通过水平的连线来表示他们是一家子
3583
+ if (i === 0) {
3584
+ wrapperElement.style.setProperty('--ft-width', '50%');
3585
+ wrapperElement.style.setProperty('--ft-left-offset', '50%');
3586
+ } else if (i > 0 && i < familyNodes.length - 1) {
3587
+ wrapperElement.style.setProperty('--ft-width', '100%');
3588
+ wrapperElement.style.setProperty('--ft-left-offset', '0px');
3589
+ } else {
3590
+ wrapperElement.style.setProperty('--ft-width', '50%');
3591
+ wrapperElement.style.setProperty('--ft-left-offset', '0px');
3592
+ }
3593
+
3594
+ wrapperElement.appendChild(nodeElement);
3595
+ hierarchyElement.appendChild(wrapperElement);
3596
+ if (this.children && this.children.length && this.children[0].length) {
3597
+ orgChart.buildInferiorNodes(wrapperElement, nodeElement, this, level);
3598
+ }
3599
+ }
3600
+ });
3601
+ });
3602
+ } else {
3603
+ if (Object.keys(data).length > 2) { // TODO: 应该用更好的方式来判断是否是供父一级节点创建的信息
3604
+ nodeElement = this.createNode(data);
3605
+ hierarchyElement.appendChild(nodeElement);
3606
+ }
3607
+ if (data.children && data.children.length) {
3608
+ this.buildInferiorNodes(hierarchyElement, nodeElement || null, data, level);
3609
+ }
3610
+ }
3611
+ },
3612
+ // build the child nodes of specific node
3613
+ buildChildNode: function (appendToEl, data) {
3614
+ const parentHierarchyEl = appendToEl;
3615
+ const existingChildNodesElement = parentHierarchyEl
3616
+ ? Array.from(parentHierarchyEl.children || []).find(function (childEl) {
3617
+ return childEl.classList && childEl.classList.contains('nodes');
3618
+ }) || null
3619
+ : null;
3620
+ const orgChart = this;
3621
+
3622
+ if (!parentHierarchyEl) {
3623
+ return;
3624
+ }
3625
+
3626
+ if (!existingChildNodesElement) {
3627
+ this.buildHierarchy(parentHierarchyEl, { 'children': data });
3628
+ return;
3629
+ }
3630
+
3631
+ forEachValue(data, function () {
3632
+ const childHierarchyElement = getDocument().createElement('li');
3633
+
3634
+ childHierarchyElement.className = 'hierarchy';
3635
+ existingChildNodesElement.appendChild(childHierarchyElement);
3636
+ orgChart.buildHierarchy(childHierarchyElement, this);
3637
+ });
3638
+ },
3639
+ // exposed method
3640
+ addChildren: function (nodeEl, data) {
3641
+ const nodeElement = getElement(nodeEl);
3642
+ let titleElement;
3643
+ let bottomEdgeElement;
3644
+
3645
+ if (!nodeElement) {
3646
+ return;
3647
+ }
3648
+
3649
+ this.buildChildNode(nodeElement.closest('.hierarchy'), data);
3650
+ if (!nodeElement.querySelector('.parentNodeSymbol')) {
3651
+ titleElement = Array.from(nodeElement.children || []).find(function (childEl) {
3652
+ return childEl.classList && childEl.classList.contains('title');
3653
+ }) || null;
3654
+ if (titleElement) {
3655
+ titleElement.insertAdjacentHTML('afterbegin', `<i class="${this.options.icons.theme} ${this.options.icons.parentNode} parentNodeSymbol"></i>`);
3656
+ }
3657
+ }
3658
+ if (nodeElement.closest('.nodes.vertical')) {
3659
+ if (!nodeElement.querySelector(':scope > .toggleBtn')) {
3660
+ nodeElement.insertAdjacentHTML('beforeend', `<i class="toggleBtn ${this.options.icons.theme}"></i>`);
3661
+ }
3662
+ } else if (!nodeElement.querySelector(':scope > .bottomEdge')) {
3663
+ nodeElement.insertAdjacentHTML('beforeend', `<i class="edge verticalEdge bottomEdge ${this.options.icons.theme}"></i>`);
3664
+ }
3665
+ if (this.isInAction(nodeElement)) {
3666
+ bottomEdgeElement = Array.from(nodeElement.children || []).find(function (childEl) {
3667
+ return childEl.classList && childEl.classList.contains('bottomEdge');
3668
+ }) || null;
3669
+ this.switchVerticalArrow(bottomEdgeElement);
3670
+ }
3671
+ },
3672
+ // build the parent node of specific node
3673
+ buildParentNode: function (currentRootEl, data) {
3674
+ const currentRootElement = getElement(currentRootEl);
3675
+ let currentRootListElement;
3676
+ let currentRootHierarchyElement;
3677
+ let parentHierarchyElement;
3678
+ let newRootWrapperElement;
3679
+ let newRootHierarchyElement;
3680
+ let newChildNodesElement;
3681
+
3682
+ if (!currentRootElement) {
3683
+ return;
3684
+ }
3685
+
3686
+ data.relationship = data.relationship || '001';
3687
+ currentRootListElement = currentRootElement.closest('ul');
3688
+ currentRootHierarchyElement = currentRootElement.closest('.hierarchy');
3689
+ parentHierarchyElement = currentRootListElement ? currentRootListElement.closest('.hierarchy') : null;
3690
+ newRootWrapperElement = getDocument().createElement('ul');
3691
+ newRootWrapperElement.className = 'nodes';
3692
+ newRootHierarchyElement = getDocument().createElement('li');
3693
+ newRootHierarchyElement.className = 'hierarchy';
3694
+ newRootHierarchyElement.appendChild(this.createNode(data));
3695
+
3696
+ if (parentHierarchyElement && currentRootHierarchyElement) {
3697
+ newChildNodesElement = getDocument().createElement('ul');
3698
+ newChildNodesElement.className = 'nodes';
3699
+ newRootHierarchyElement.appendChild(newChildNodesElement);
3700
+ currentRootListElement.replaceChild(newRootHierarchyElement, currentRootHierarchyElement);
3701
+ newChildNodesElement.appendChild(currentRootHierarchyElement);
3702
+ return;
3703
+ }
3704
+
3705
+ newRootWrapperElement.appendChild(newRootHierarchyElement);
3706
+ if (this.chart) {
3707
+ this.chart.prepend(newRootWrapperElement);
3708
+ }
3709
+ if (currentRootListElement) {
3710
+ currentRootListElement.classList.add('nodes');
3711
+ newRootHierarchyElement.appendChild(currentRootListElement);
3712
+ }
3713
+ },
3714
+ // exposed method
3715
+ addParent: function (currentRootEl, data) {
3716
+ const currentRootElement = getElement(currentRootEl);
3717
+ let titleElement;
3718
+ let topEdgeElement;
3719
+
3720
+ if (!currentRootElement) {
3721
+ return;
3722
+ }
3723
+
3724
+ this.buildParentNode(currentRootElement, data);
3725
+ if (!currentRootElement.querySelector(':scope > .topEdge')) {
3726
+ titleElement = Array.from(currentRootElement.children || []).find(function (childEl) {
3727
+ return childEl.classList && childEl.classList.contains('title');
3728
+ }) || null;
3729
+ if (titleElement) {
3730
+ titleElement.insertAdjacentHTML('afterend', `<i class="edge verticalEdge topEdge ${this.options.icons.theme}"></i>`);
3731
+ }
3732
+ }
3733
+ if (this.isInAction(currentRootElement)) {
3734
+ topEdgeElement = Array.from(currentRootElement.children || []).find(function (childEl) {
3735
+ return childEl.classList && childEl.classList.contains('topEdge');
3736
+ }) || null;
3737
+ this.switchVerticalArrow(topEdgeElement);
3738
+ }
3739
+ },
3740
+ // build the sibling nodes of specific node
3741
+ buildSiblingNode: function (nodeChartEl, data) {
3742
+ const nodeChartElement = getElement(nodeChartEl);
3743
+ let nodeChartParentElement;
3744
+ let newSiblingCount;
3745
+ let existingSiblingCount;
3746
+ let siblingCount;
3747
+ let insertPosition;
3748
+ let ancestorNodesElement;
3749
+ let parentHierarchyElement;
3750
+ let newSiblingContainerElement;
3751
+ let newSiblingHierarchyElements;
3752
+ let existingHierarchyElements;
3753
+ let newParentHierarchyElement;
3754
+ let newParentChildrenContainerElement;
3755
+ let insertAnchorElement;
3756
+ const removeIfEmpty = function (containerElement) {
3757
+ if (containerElement && !containerElement.children.length) {
3758
+ containerElement.remove();
3759
+ }
3760
+ };
3761
+ const getHierarchyChildren = function (containerElement) {
3762
+ return containerElement
3763
+ ? Array.from(containerElement.children).filter(function (childEl) {
3764
+ return childEl.classList && childEl.classList.contains('hierarchy');
3765
+ })
3766
+ : [];
3767
+ };
3768
+ const getLastNodesChild = function (containerElement) {
3769
+ let childElement;
3770
+
3771
+ if (!containerElement) {
3772
+ return null;
3773
+ }
3774
+
3775
+ childElement = containerElement.lastElementChild;
3776
+ while (childElement) {
3777
+ if (childElement.classList && childElement.classList.contains('nodes')) {
3778
+ return childElement;
3779
+ }
3780
+ childElement = childElement.previousElementSibling;
3781
+ }
3782
+
3783
+ return null;
3784
+ };
3785
+
3786
+ if (!nodeChartElement) {
3787
+ return;
3788
+ }
3789
+
3790
+ nodeChartParentElement = nodeChartElement.parentElement;
3791
+ newSiblingCount = Array.isArray(data) ? data.length : data.children.length;
3792
+ existingSiblingCount = nodeChartParentElement && nodeChartParentElement.classList && nodeChartParentElement.classList.contains('nodes')
3793
+ ? getHierarchyChildren(nodeChartParentElement).length
3794
+ : 1;
3795
+ siblingCount = existingSiblingCount + newSiblingCount;
3796
+ insertPosition = (siblingCount > 1) ? Math.floor(siblingCount / 2 - 1) : 0;
3797
+ // just build the sibling nodes for the specific node
3798
+ ancestorNodesElement = nodeChartElement.closest('.nodes');
3799
+ if (ancestorNodesElement && ancestorNodesElement.parentElement && ancestorNodesElement.parentElement.classList && ancestorNodesElement.parentElement.classList.contains('hierarchy')) {
3800
+ parentHierarchyElement = nodeChartParentElement ? nodeChartParentElement.closest('.hierarchy') : null;
3801
+ this.buildChildNode(parentHierarchyElement, data);
3802
+ newSiblingContainerElement = getLastNodesChild(parentHierarchyElement);
3803
+ newSiblingHierarchyElements = getHierarchyChildren(newSiblingContainerElement);
3804
+ if (existingSiblingCount > 1 && newSiblingContainerElement !== nodeChartParentElement) {
3805
+ existingHierarchyElements = getHierarchyChildren(nodeChartParentElement);
3806
+ existingHierarchyElements.forEach(function (existingSiblingEl) {
3807
+ if (newSiblingHierarchyElements[0] && newSiblingHierarchyElements[0].parentElement) {
3808
+ newSiblingHierarchyElements[0].parentElement.insertBefore(existingSiblingEl, newSiblingHierarchyElements[0]);
3809
+ }
3810
+ });
3811
+ removeIfEmpty(nodeChartParentElement);
3812
+ } else if (newSiblingContainerElement !== nodeChartParentElement) {
3813
+ insertAnchorElement = newSiblingHierarchyElements[insertPosition] || null;
3814
+ if (insertAnchorElement && insertAnchorElement.parentElement) {
3815
+ insertAnchorElement.parentElement.insertBefore(nodeChartElement, insertAnchorElement.nextSibling);
3816
+ }
3817
+ removeIfEmpty(nodeChartParentElement);
3818
+ }
3819
+ } else { // build the sibling nodes and parent node for the specific ndoe
3820
+ newParentHierarchyElement = getDocument().createElement('li');
3821
+ newParentHierarchyElement.className = 'hierarchy';
3822
+ if (nodeChartParentElement) {
3823
+ nodeChartParentElement.insertBefore(newParentHierarchyElement, nodeChartParentElement.firstChild);
3824
+ }
3825
+ this.buildHierarchy(newParentHierarchyElement, data);
3826
+ newParentChildrenContainerElement = getLastNodesChild(newParentHierarchyElement);
3827
+ insertAnchorElement = newParentChildrenContainerElement
3828
+ ? getHierarchyChildren(newParentChildrenContainerElement)[insertPosition] || null
3829
+ : null;
3830
+ if (insertAnchorElement && insertAnchorElement.parentElement) {
3831
+ insertAnchorElement.parentElement.insertBefore(nodeChartElement, insertAnchorElement.nextSibling);
3832
+ }
3833
+ }
3834
+ },
3835
+ //
3836
+ addSiblings: function (nodeEl, data) {
3837
+ const nodeElement = getElement(nodeEl);
3838
+ let nodesContainerElement;
3839
+ let topEdgeElement;
3840
+
3841
+ if (!nodeElement) {
3842
+ return;
3843
+ }
3844
+
3845
+ this.buildSiblingNode(nodeElement.closest('.hierarchy'), data);
3846
+ nodesContainerElement = nodeElement.closest('.nodes');
3847
+ if (nodesContainerElement) {
3848
+ setState(nodesContainerElement, 'siblingsLoaded', true);
3849
+ }
3850
+ if (!Array.from(nodeElement.children || []).some(function (childEl) {
3851
+ return childEl.classList && childEl.classList.contains('leftEdge');
3852
+ })) {
3853
+ topEdgeElement = Array.from(nodeElement.children || []).find(function (childEl) {
3854
+ return childEl.classList && childEl.classList.contains('topEdge');
3855
+ }) || null;
3856
+ if (topEdgeElement) {
3857
+ topEdgeElement.insertAdjacentHTML('afterend', `<i class="edge horizontalEdge rightEdge ${this.options.icons.theme}"></i><i class="edge horizontalEdge leftEdge ${this.options.icons.theme}"></i>`);
3858
+ }
3859
+ }
3860
+ if (this.isInAction(nodeElement)) {
3861
+ this.switchHorizontalArrow(nodeElement);
3862
+ topEdgeElement = Array.from(nodeElement.children || []).find(function (childEl) {
3863
+ return childEl.classList && childEl.classList.contains('topEdge');
3864
+ }) || null;
3865
+ if (topEdgeElement && topEdgeElement.classList) {
3866
+ topEdgeElement.classList.remove(this.options.icons.expandToUp);
3867
+ topEdgeElement.classList.add(this.options.icons.collapseToDown);
3868
+ }
3869
+ }
3870
+ },
3871
+ // remove node and its descendent nodes
3872
+ removeNodes: function (nodeEl) {
3873
+ const nodeElement = getElement(nodeEl);
3874
+ let hierarchyElement;
3875
+ let wrapperElement;
3876
+ let remainingNodeElement;
3877
+ let parentNodeElement;
3878
+
3879
+ if (!nodeElement) {
3880
+ return;
3881
+ }
3882
+
3883
+ hierarchyElement = nodeElement.closest('.hierarchy');
3884
+ wrapperElement = hierarchyElement ? hierarchyElement.parentElement : null;
3885
+
3886
+ if (!wrapperElement) {
3887
+ return;
3888
+ }
3889
+
3890
+ if (wrapperElement.parentElement && wrapperElement.parentElement.classList && wrapperElement.parentElement.classList.contains('hierarchy')) {
3891
+ if (this.getNodeState(nodeElement, 'siblings').exist) {
3892
+ hierarchyElement.remove();
3893
+ if (wrapperElement.children.length === 1) {
3894
+ remainingNodeElement = wrapperElement.querySelector('.node');
3895
+ if (remainingNodeElement) {
3896
+ Array.from(remainingNodeElement.querySelectorAll('.horizontalEdge')).forEach(function (edgeEl) {
3897
+ edgeEl.remove();
3898
+ });
3899
+ }
3900
+ }
3901
+ } else {
3902
+ parentNodeElement = wrapperElement.parentElement.querySelector(':scope > .node');
3903
+ if (parentNodeElement) {
3904
+ Array.from(parentNodeElement.querySelectorAll('.bottomEdge, .parentNodeSymbol')).forEach(function (edgeEl) {
3905
+ edgeEl.remove();
3906
+ });
3907
+ }
3908
+ wrapperElement.remove();
3909
+ }
3910
+ } else { // if $node is root node
3911
+ const chartElement = wrapperElement.closest('.orgchart');
3912
+ if (chartElement) {
3913
+ chartElement.remove();
3914
+ }
3915
+ }
3916
+ },
3917
+ //
3918
+ hideDropZones: function () {
3919
+ const chartElement = this.chart;
3920
+
3921
+ if (!chartElement) {
3922
+ return;
3923
+ }
3924
+
3925
+ // Remove all the 'this is a drop zone' indicators
3926
+ Array.from(chartElement.querySelectorAll('.allowedDrop')).forEach(function (node) {
3927
+ node.classList.remove('allowedDrop');
3928
+ });
3929
+ },
3930
+ //
3931
+ showDropZones: function (draggedEl) {
3932
+ // Highlight all the 'drop zones', and set dragged, so that the drop/enter can work out what happens later
3933
+ // TODO: This assumes all nodes are droppable: it doesn't run the custom isDroppable function - it should!
3934
+ const draggedNodeEl = getElement(draggedEl);
3935
+ const chartElement = this.chart;
3936
+
3937
+ if (!chartElement) {
3938
+ return;
3939
+ }
3940
+
3941
+ Array.from(chartElement.querySelectorAll('.node')).forEach(function (node) {
3942
+ node.classList.add('allowedDrop');
3943
+ });
3944
+ setState(chartElement, 'dragged', draggedNodeEl);
3945
+ },
3946
+ //
3947
+ processExternalDrop: function (dropTargetEl, draggedEl) {
3948
+ const dropZoneEl = getElement(dropTargetEl);
3949
+ const droppedOnNodeEl = dropZoneEl && typeof dropZoneEl.closest === 'function'
3950
+ ? dropZoneEl.closest('.node')
3951
+ : null;
3952
+ let draggedNodeEl;
3953
+ const chartElement = this.chart;
3954
+
3955
+ // Allow an external drop event to be handled by one of our nodes
3956
+ if (draggedEl && chartElement) {
3957
+ draggedNodeEl = getElement(draggedEl);
3958
+ setState(chartElement, 'dragged', draggedNodeEl);
3959
+ }
3960
+ if (!droppedOnNodeEl) {
3961
+ return;
3962
+ }
3963
+ // would like to just call 'dropZoneHandler', but I can't reach it from here
3964
+ // instead raise a drop event on the node element
3965
+ droppedOnNodeEl.dispatchEvent(createTriggeredEvent('drop'));
3966
+ },
3967
+ //
3968
+ exportPDF: function(canvas, exportFilename, exportScale){
3969
+ let doc = {};
3970
+ const scale = exportScale > 0 ? exportScale : 1;
3971
+ const docWidth = Math.floor(canvas.width / scale);
3972
+ const docHeight = Math.floor(canvas.height / scale);
3973
+ if (!getWindow().jsPDF) {
3974
+ getWindow().jsPDF = getWindow().jspdf.jsPDF;
3975
+ }
3976
+
3977
+ if (docWidth > docHeight) {
3978
+ doc = new jsPDF({
3979
+ orientation: 'landscape',
3980
+ unit: 'px',
3981
+ format: [docWidth, docHeight]
3982
+ });
3983
+ } else {
3984
+ doc = new jsPDF({
3985
+ orientation: 'portrait',
3986
+ unit: 'px',
3987
+ format: [docHeight, docWidth]
3988
+ });
3989
+ }
3990
+ doc.addImage(canvas.toDataURL(), 'png', 0, 0, docWidth, docHeight);
3991
+ doc.save(exportFilename + '.pdf');
3992
+ },
3993
+ //
3994
+ exportPNG: function(canvas, exportFilename){
3995
+ const isWebkit = 'WebkitAppearance' in getDocument().documentElement.style;
3996
+ const isFf = !!getWindow().sidebar;
3997
+ const navigatorObject = getWindow().navigator;
3998
+ const isEdge = navigatorObject.appName === 'Microsoft Internet Explorer' || (navigatorObject.appName === 'Netscape' && navigatorObject.appVersion.indexOf('Edge') > -1);
3999
+ const chartContainerElement = this.chartContainer;
4000
+ let downloadButtonClassName;
4001
+ let downloadSelector;
4002
+ let downloadAnchorElement;
4003
+
4004
+ if (!chartContainerElement) {
4005
+ return;
4006
+ }
4007
+
4008
+ if ((!isWebkit && !isFf) || isEdge) {
4009
+ getWindow().navigator.msSaveBlob(canvas.msToBlob(), exportFilename + '.png');
4010
+ } else {
4011
+ downloadButtonClassName = 'download-btn' + (this.options.chartClass !== '' ? ' ' + this.options.chartClass : '');
4012
+ downloadSelector = '.download-btn' + (this.options.chartClass !== '' ? '.' + this.options.chartClass : '');
4013
+ downloadAnchorElement = chartContainerElement.querySelector(downloadSelector);
4014
+
4015
+ if (!downloadAnchorElement) {
4016
+ downloadAnchorElement = getDocument().createElement('a');
4017
+ downloadAnchorElement.className = downloadButtonClassName;
4018
+ chartContainerElement.appendChild(downloadAnchorElement);
4019
+ }
4020
+
4021
+ downloadAnchorElement.setAttribute('download', exportFilename + '.png');
4022
+ downloadAnchorElement.setAttribute('href', canvas.toDataURL());
4023
+ downloadAnchorElement.click();
4024
+ }
4025
+ },
4026
+ //
4027
+ export: function (exportFilename, exportFileextension) {
4028
+ const orgChart = this;
4029
+ const chartContainerElement = this.chartContainer;
4030
+ let maskElement;
4031
+ let sourceChartElement;
4032
+ let isHorizontalDirection;
4033
+ let exportScale;
4034
+
4035
+ exportFilename = (typeof exportFilename !== 'undefined') ? exportFilename : this.options.exportFilename;
4036
+ exportFileextension = (typeof exportFileextension !== 'undefined') ? exportFileextension : this.options.exportFileextension;
4037
+ if (chartContainerElement && chartContainerElement.querySelector('.spinner')) {
4038
+ return false;
4039
+ }
4040
+
4041
+ if (!chartContainerElement) {
4042
+ return false;
4043
+ }
4044
+
4045
+ maskElement = chartContainerElement.querySelector('.mask');
4046
+ if (!maskElement) {
4047
+ maskElement = getDocument().createElement('div');
4048
+ maskElement.className = 'mask';
4049
+ maskElement.insertAdjacentHTML('beforeend', `<i class="${this.options.icons.theme} ${this.options.icons.spinner} spinner"></i>`);
4050
+ chartContainerElement.appendChild(maskElement);
4051
+ } else {
4052
+ maskElement.classList.remove('hidden');
4053
+ }
4054
+ chartContainerElement.classList.add('canvasContainer');
4055
+ sourceChartElement = Array.from(chartContainerElement.querySelectorAll('.orgchart')).find(function (chartEl) {
4056
+ return !chartEl.classList.contains('hidden');
4057
+ }) || null;
4058
+ isHorizontalDirection = orgChart.options.direction === 'l2r' || orgChart.options.direction === 'r2l';
4059
+ exportScale = orgChart.options.exportScale > 0 ? orgChart.options.exportScale : 1;
4060
+ html2canvas(sourceChartElement, {
4061
+ 'width': isHorizontalDirection ? sourceChartElement.clientHeight : sourceChartElement.clientWidth,
4062
+ 'height': isHorizontalDirection ? sourceChartElement.clientWidth : sourceChartElement.clientHeight,
4063
+ 'scale': exportScale,
4064
+ 'useCORS': true,
4065
+ 'onclone': function (cloneDoc) {
4066
+ const clonedContainerElement = cloneDoc.querySelector('.canvasContainer');
4067
+ let clonedChartElement;
4068
+
4069
+ if (!clonedContainerElement) {
4070
+ return;
4071
+ }
4072
+
4073
+ clonedContainerElement.style.overflow = 'visible';
4074
+ clonedChartElement = Array.from(clonedContainerElement.querySelectorAll('.orgchart')).find(function (chartEl) {
4075
+ return !chartEl.classList.contains('hidden');
4076
+ }) || null;
4077
+ if (clonedChartElement) {
4078
+ clonedChartElement.style.transform = '';
4079
+ }
4080
+ }
4081
+ })
4082
+ .then(function (canvas) {
4083
+ if (maskElement) {
4084
+ maskElement.classList.add('hidden');
4085
+ }
4086
+
4087
+ if (exportFileextension.toLowerCase() === 'pdf') {
4088
+ orgChart.exportPDF(canvas, exportFilename, exportScale);
4089
+ } else {
4090
+ orgChart.exportPNG(canvas, exportFilename);
4091
+ }
4092
+
4093
+ chartContainerElement.classList.remove('canvasContainer');
4094
+ }, function () {
4095
+ chartContainerElement.classList.remove('canvasContainer');
4096
+ });
4097
+ }
4098
+ };
4099
+
4100
+ return OrgChart;
4101
+ }));