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