PluginProbe
Chartify – WordPress Chart Plugin / 3.2.7
Chartify – WordPress Chart Plugin v3.2.7
3.8.0 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 trunk 1.0.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 All 83 releases
chart-builder / admin / js / treeSortable.js

treeSortable.js in Chartify – WordPress Chart Plugin 3.2.7, at admin/js/treeSortable.js

831 lines 35.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Tree sortable jQuery library using jQuery UI sortable.
3 *
4 * @package TreeSortable
5 * @license MIT
6 * @author Sajeeb Ahamed <sajeeb07ahamed@gmail.com>
7 */
8
9 const $ = jQuery;
10
11 const getCenterPosition = element => {
12 const { top, left, width, height } = element.getBoundingClientRect();
13
14 return {
15 x: left + width / 2,
16 y: top + height / 2,
17 };
18 };
19
20 const getDistance = (elementA, elementB) => {
21 const positionA = getCenterPosition(elementA);
22 const positionB = getCenterPosition(elementB);
23
24 const distanceX = Math.floor(Math.abs(positionA.x - positionB.x));
25 const distanceY = Math.floor(Math.abs(positionA.y - positionB.y));
26
27 return { distanceX, distanceY };
28 };
29
30 function TreeSortable(options) {
31 this.cssVarPrefix = 'tree-sortable';
32 this.defaults = {
33 depth: 30,
34 treeSelector: '#tree',
35 branchSelector: '.tree-branch',
36 branchPathSelector: '.branch-path',
37 dragHandlerSelector: '.branch-drag-handler',
38 placeholderName: 'sortable-placeholder',
39 childrenBusSelector: '.children-bus',
40 levelPrefix: 'branch-level',
41 maxLevel: 100,
42 dataAttributes: {
43 id: 'id',
44 parent: 'parent',
45 level: 'level',
46 },
47 };
48
49 this.options = { ...this.defaults, ...options };
50
51 const self = this;
52
53 this.run = function () {
54 this.jQuerySupplements();
55 this.initSorting();
56 this.addCSSVars();
57 this.branchesLeftShifting();
58 };
59
60 this.addCSSVars = function () {
61 const variables = {
62 depth: { value: self.options.depth, unit: 'px' },
63 };
64
65 const root = $(self.options.treeSelector);
66
67 for (const name in variables) {
68 const value = variables[name];
69 root.get(0).style.setProperty(`--${self.cssVarPrefix}-${name}`, `${value.value}${value.unit || ''}`);
70 }
71 };
72
73 this.addBranchLeftShift = function ($element) {
74 const { depth } = self.options;
75 const level = Number($element.data('level')) || 1;
76
77 $element.get(0).style.setProperty(`--${self.cssVarPrefix}-branch-left-shift`, `${depth * (level - 1)}px`);
78 $element.get(0).style.setProperty(`--${self.cssVarPrefix}-children-left-shift`, `-${depth * (level - 1)}px`);
79 };
80
81 this.branchesLeftShifting = function () {
82 const $elements = $(`${self.options.treeSelector} ${self.options.branchSelector}`);
83 $elements.each(function () {
84 self.addBranchLeftShift($(this));
85 });
86 };
87
88 this.cleanSelector = function (selector) {
89 return selector.replace(/^[\.#]/g, '');
90 };
91
92 this.getInstance = function () {
93 return this;
94 };
95
96 this.getTreeEdge = function () {
97 return $(this.options.treeSelector).offset().left;
98 };
99 this.pxToNumber = function (str) {
100 return new RegExp('px$', 'i').test(str) ? str.slice(0, -2) * 1 : 0;
101 };
102 this.numberToPx = function (num) {
103 return `${num}px`;
104 };
105 this.onSortCompleted = function (callback) {
106 $(this.options.treeSelector).on('sortCompleted', callback);
107 };
108
109 this.generateUUID = function () {
110 return 'xxxxxxxx-xxx'.replace(/[xy]/g, function (c) {
111 var r = (Math.random() * 16) | 0,
112 v = c == 'x' ? r : (r & 0x3) | 0x8;
113 return v.toString(16);
114 });
115 };
116 this.createBranch = function ({ id, parent_id, title, level, description = "", image = "", tooltip = "", url = "", parent_name }) {
117 const {
118 branchSelector,
119 branchPathSelector,
120 dragHandlerSelector,
121 childrenBusSelector,
122 levelPrefix,
123 dataAttributes: { id: idAttr, parent: parentAttr, level: levelAttr },
124 } = self.options;
125
126 if (typeof parent_name == "undefined") {
127 parent_name = $(".tree-branch[data-id=" + parent_id + "]").find(".ays-chart-org-chart-name-" + parent_id).val();
128 }
129
130 var buttons = `<button type="button" class="button add-child" data-tippy-content="Add a new child">
131 <i class="fa fa-plus-circle"></i>
132 </button>`;
133 if (id != 1) {
134 buttons += `<button type="button" class="button add-sibling" data-tippy-content="Add a new sibling">
135 <i class="fa fa-plus-square"></i>
136 </button>
137 <button type="button" class="button remove-branch" data-tippy-content="Remove Branch">
138 <i class="fa fa-trash"></i>
139 </button>`;
140 }
141 buttons += `<button type="button" class="button open-branch-options" data-tippy-content="Change options" data-source-id=${id}>
142 <i class="fa fa-caret-down"></i>
143 </button>`;
144
145 return `
146 <li class="${self.cleanSelector(
147 branchSelector
148 )} ${levelPrefix}-${level}" data-${idAttr}="${id}" data-${parentAttr}="${parent_id}" data-${levelAttr}="${level}">
149 <div class="contents">
150 <span class="${self.cleanSelector(branchPathSelector)}"></span>
151 <div class="branch-wrapper">
152 <div class="sidebars">
153 <div class="left-sidebar">
154 <div class="${self.cleanSelector(dragHandlerSelector)}">
155 <i class="fa fa-arrows"></i>
156 </div>
157 <span class="branch-title">${title}</span>
158 </div>
159 <div class="right-sidebar">${buttons}</div>
160 </div>
161 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-name-${id}" value="${title}">
162 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-description-${id}" value="${description}">
163 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-image-${id}" value="${image}">
164 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-parent-name" value="${parent_name}">
165 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-tooltip-${id}" value="${tooltip}">
166 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-url-${id}" value="${url}">
167 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-parent" value="${parent_id}">
168 <input type="hidden" name="ays_chart_source_data_org_type[id_${id}][]" class="ays-chart-org-chart-level" value="${level}">
169 </div>
170 </div>
171 <div class="${self.cleanSelector(childrenBusSelector)}"></div>
172 </li>
173 `;
174 };
175
176 this.addListener = function (event, selector, callback) {
177 $(document).on(event, `${self.options.treeSelector} ${selector}`, function (e) {
178 callback(e, self);
179 });
180 };
181
182 this.jQuerySupplements = function () {
183 const { levelPrefix, dataAttributes } = self.options;
184 $.fn.extend({
185 getBranchLevel() {
186 return Number($(this).data('level')) || 0;
187 },
188 updateBranchLevel(current, prev = null) {
189 return this.each(function () {
190 prev = prev || $(this).getBranchLevel() || 1;
191 $(this)
192 .removeClass(levelPrefix + '-' + prev)
193 .addClass(levelPrefix + '-' + current)
194 .data(dataAttributes.level, current)
195 .attr(`data-${dataAttributes.level}`, current)
196 .find($(".ays-chart-org-chart-level")).val(current);
197 self.addBranchLeftShift($(this));
198 });
199 },
200 shiftBranchLevel(dx) {
201 return this.each(function () {
202 let level = $(this).getBranchLevel() || 1,
203 newLevel = level + dx;
204
205 $(this)
206 .removeClass(levelPrefix + '-' + level)
207 .addClass(levelPrefix + '-' + newLevel)
208 .data(dataAttributes.level, newLevel)
209 .attr(`data-${dataAttributes.level}`, newLevel)
210 .find($(".ays-chart-org-chart-level")).val(newLevel);
211 self.addBranchLeftShift($(this));
212 });
213 },
214 getParent() {
215 const { branchSelector } = self.options;
216 const level = $(this).getBranchLevel() || 1;
217 let $prev = $(this).prev(branchSelector);
218
219 while ($prev.length && $prev.getBranchLevel() >= level) {
220 $prev = $prev.prev(branchSelector);
221 }
222
223 return $prev;
224 },
225 getRootChild() {
226 const { branchSelector, treeSelector, levelPrefix } = self.options;
227
228 return $(treeSelector).children(`${branchSelector}.${levelPrefix}-1`);
229 },
230 getLastChild() {
231 const { branchSelector, treeSelector, levelPrefix } = self.options;
232 const $children = $(this).getChildren();
233 const $descendants = $(this).getDescendants();
234 const $lastChild = $descendants.length > $children.length ? $descendants.last() : $children.last();
235
236 return $lastChild.length ? $lastChild : $();
237 },
238 getChildren() {
239 const { branchSelector } = self.options;
240 let $children = $();
241
242 this.each(function () {
243 let level = $(this).getBranchLevel() || 1,
244 $next = $(this).next(branchSelector);
245
246 while ($next.length && $next.getBranchLevel() > level) {
247 if ($next.getBranchLevel() === level + 1) {
248 $children = $children.add($next);
249 }
250 $next = $next.next(branchSelector);
251 }
252 });
253
254 return $children;
255 },
256 getDescendants() {
257 const { branchSelector } = self.options;
258 let $descendants = $();
259
260 this.each(function () {
261 let level = $(this).getBranchLevel() || 1,
262 $next = $(this).next(branchSelector);
263
264 while ($next.length && $next.getBranchLevel() > level) {
265 $descendants = $descendants.add($next);
266 $next = $next.next(branchSelector);
267 }
268 });
269
270 return $descendants;
271 },
272 nextBranch() {
273 return $(this).next();
274 },
275 prevBranch() {
276 return $(this).prev();
277 },
278 nextSibling() {
279 const { branchSelector } = self.options;
280
281 let level = $(this).getBranchLevel() || 1,
282 $next = $(this).next(branchSelector),
283 nextLevel = $next.getBranchLevel();
284
285 while ($next.length && nextLevel > level) {
286 $next = $next.next(branchSelector);
287 nextLevel = $next.getBranchLevel();
288 }
289
290 return +nextLevel === +level ? $next : $();
291 },
292 prevSibling() {
293 const { branchSelector } = self.options;
294 let level = $(this).getBranchLevel() || 1,
295 $prev = $(this).prev(branchSelector),
296 prevLevel = $prev.getBranchLevel();
297
298 while ($prev.length && prevLevel > level) {
299 $prev = $prev.prev(branchSelector);
300 prevLevel = $prev.getBranchLevel();
301 }
302
303 return prevLevel === level ? $prev : $();
304 },
305 getLastSibling() {
306 let $nextSibling = $(this).nextSibling();
307
308 if (!$nextSibling.length) {
309 return $(this);
310 }
311
312 while ($nextSibling.length) {
313 $temp = $nextSibling.nextSibling();
314
315 if ($temp.length) {
316 $nextSibling = $temp;
317 } else {
318 return $nextSibling;
319 }
320 }
321 },
322 getSiblings(level = null) {
323 const { treeSelector, branchSelector } = self.options;
324 level = level || $(this).getBranchLevel();
325
326 let $siblings = [],
327 $branches = $(`${treeSelector} > ${branchSelector}`),
328 $self = this;
329
330 $branches.length &&
331 $branches.each(function () {
332 let branchLevel = $(this).getBranchLevel();
333
334 if (+branchLevel === +level && $self[0] !== $(this)[0]) {
335 $siblings.push($(this));
336 }
337 });
338
339 return $siblings;
340 },
341 calculateSiblingDistances() {
342 const { branchSelector, branchPathSelector } = self.options;
343
344 $(branchSelector).each(function () {
345 const level = $(this).getBranchLevel() || 1;
346 $(this).find(branchPathSelector).show();
347
348 if (typeof $(this).nextSibling !== 'function') return;
349
350 if (level > 1) {
351 const $sibling = $(this).nextSibling();
352
353 /**
354 * If next sibling (siblings with same branch level) exists then
355 * calculate the distance between two siblings and set the path
356 * height according to the distance.
357 */
358 if ($sibling.length) {
359 const distance = getDistance($(this).get(0), $sibling.get(0));
360 const thisParent = $(this).getParent() || $(this).getRootChild();
361 const parentDistance = getDistance($(this).get(0), thisParent.get(0));
362
363 $sibling
364 .find(branchPathSelector)
365 .css('height', `${Math.max(distance.distanceY + 8, 55)}px`);
366 $(this)
367 .find(branchPathSelector)
368 .css('height', `${Math.max(parentDistance.distanceY + 8, 55)}px`);
369 } else {
370 /**
371 * If no sibling exists to a branch then find the child.
372 * If child exists then set the child height as the default 55px.
373 */
374 const $nextBranch = $(this).next(branchSelector);
375 const nextBranchLevel = $nextBranch.getBranchLevel() || 1;
376
377 const isChild = $nextBranch.length > 0 && nextBranchLevel > level;
378
379 if (isChild) {
380 $nextBranch.find(branchPathSelector).css('height', '55px');
381 }
382
383 if ($nextBranch.length > 0 && nextBranchLevel < level) {
384 if ($(this).prevBranch().getBranchLevel() <= level) {
385 $(this).find(branchPathSelector).css('height', '72px');
386 }
387 }
388
389 if ($(this).prevBranch().getBranchLevel() < level) {
390 $(this).find(branchPathSelector).css('height', '72px');
391 }
392 }
393 } else {
394 $(this).find(branchPathSelector).hide();
395 }
396 });
397 },
398 detectManualChange() {
399 var input = $(document).find('input[name="ays_source_type"]');
400 if (input.val() !== 'manual') {
401 input.val('manual');
402 }
403 }
404 });
405 };
406
407 this.addChildBranch = function ($triggerElement, treeDataSource) {
408 const {
409 treeSelector,
410 branchSelector,
411 dataAttributes: { id },
412 maxLevel,
413 } = self.options;
414 $branch = $triggerElement.closest(`${treeSelector} ${branchSelector}`);
415
416 if (!$branch.length) {
417 throw Error('Invalid selector! Make sure that your add child button is inside a branch.');
418 }
419
420 const uid = treeDataSource.length + 1;
421 const parent_id = $branch.data(id);
422 const level = Math.min(maxLevel, parseInt($branch.getBranchLevel()) + 1);
423 const title = 'New Branch ' + uid;
424
425 $lastChild = $branch.getLastChild();
426
427 const $element = self.createBranch({ id: uid, parent_id, title, level });
428
429 if ($lastChild.length) {
430 $lastChild.after($element);
431 } else {
432 $branch.after($element);
433 }
434
435 $(treeSelector).detectManualChange();
436 $(treeSelector).calculateSiblingDistances();
437 self.updateBranchZIndex();
438 self.branchesLeftShifting();
439
440 treeDataSource.push({
441 id: uid,
442 parent_id: parent_id,
443 title: title,
444 level: level
445 })
446 };
447
448 this.addSiblingBranch = function ($triggerElement, treeDataSource) {
449 const {
450 treeSelector,
451 branchSelector,
452 dataAttributes: { parent },
453 } = self.options;
454
455 const $branch = $triggerElement.closest(`${treeSelector} ${branchSelector}`);
456 const uid = treeDataSource.length + 1;
457 const parent_id = $branch.data(parent);
458 const level = $branch.getBranchLevel();
459 const title = 'New Branch ' + uid;
460 const $lastSibling = $branch.getLastSibling();
461 let $lastChild = $lastSibling.getLastChild();
462
463 while ($lastChild.length) {
464 $temp = $lastChild.getLastChild();
465
466 if ($temp.length) {
467 $lastChild = $temp;
468 } else {
469 break;
470 }
471 }
472
473 const $element = self.createBranch({ id: uid, parent_id, level, title });
474
475 if ($lastChild.length) {
476 $lastChild.after($element);
477 } else {
478 $lastSibling.after($element);
479 }
480
481 $(treeSelector).detectManualChange();
482 $(treeSelector).calculateSiblingDistances();
483 self.updateBranchZIndex();
484 self.branchesLeftShifting();
485
486 treeDataSource.push({
487 id: uid,
488 parent_id: parent_id,
489 title: title,
490 level: level
491 })
492 };
493
494 this.removeBranch = function ($triggerElement) {
495 const { treeSelector, branchSelector } = self.options;
496
497 const $branch = $triggerElement.closest(`${treeSelector} ${branchSelector}`);
498 $descendants = $branch.getDescendants();
499
500 $descendants.each((_, element) => {
501 $(element).remove();
502 });
503
504 $branch.remove();
505 $(treeSelector).detectManualChange();
506 self.updateBranchZIndex();
507 self.branchesLeftShifting();
508 $(treeSelector).calculateSiblingDistances();
509 };
510
511 this.openBranchOptions = function ($triggerElement) {
512 var currentBranchWrapper = $triggerElement.parents(".branch-wrapper");
513
514 var openCloseButton = $triggerElement.parent();
515 var openCloseIcon =$triggerElement;
516 if (!$triggerElement.hasClass("fa")) {
517 var openCloseButton = $triggerElement;
518 var openCloseIcon = openCloseButton.children();
519 }
520
521 var currentBranchId = openCloseButton.attr("data-source-id");
522 var branchNameInpValue = currentBranchWrapper.find(".ays-chart-org-chart-name-" + currentBranchId).val();
523 var branchDescriptionInpValue = currentBranchWrapper.find(".ays-chart-org-chart-description-" + currentBranchId).val();
524 var branchImageText = aysChartBuilderAdmin.addImage;
525 var branchTooltipInpValue = currentBranchWrapper.find(".ays-chart-org-chart-tooltip-" + currentBranchId).val();
526 var branchUrlInpValue = currentBranchWrapper.find(".ays-chart-org-chart-url-" + currentBranchId).val();
527
528 var branchOptions = `
529 <div class="ays-chart-org-chart-manual-branch-options-container">
530 <div class="ays-chart-org-chart-manual-branch-options-left-box">
531 <div class="ays-chart-org-chart-branch-main-options-box">
532 <label for="ays-chart-org-chart-branch-name">Name:</label>
533 <input type="text" id="ays-chart-org-chart-branch-name" name="ays-chart-org-chart-branch-name" placeholder="Name" value="${branchNameInpValue}">
534 </div>
535 <div class="ays-chart-org-chart-branch-main-options-box">
536 <label for="ays-chart-org-chart-branch-description">Description:</label>
537 <input type="text" id="ays-chart-org-chart-branch-description" name="ays-chart-org-chart-branch-description" placeholder="Description" value="${branchDescriptionInpValue}">
538 </div>
539 <div class="ays-chart-org-chart-branch-main-options-box">
540 <label>Image:</label>
541 <a class="button ays-chart-select-org-img" href="https://ays-pro.com/wordpress/chart-builder" target="_blank" disabled>${branchImageText}</a>
542 </div>
543 <div class="ays-chart-org-chart-branch-main-options-box">
544 <label for="ays-chart-org-chart-branch-tooltip">Tooltip:</label>
545 <input type="text" id="ays-chart-org-chart-branch-tooltip" name="ays-chart-org-chart-branch-tooltip" placeholder="Tooltip" value="${branchTooltipInpValue}">
546 </div>
547 <div class="ays-chart-org-chart-branch-main-options-box">
548 <label for="ays-chart-org-chart-branch-url">Url:</label>
549 <input type="text" id="ays-chart-org-chart-branch-url" name="ays-chart-org-chart-branch-url" placeholder="Url" value="${branchUrlInpValue}">
550 </div>
551 </div>
552 <div class="ays-chart-org-chart-manual-branch-options-rigth-box">
553 <button class="button change-branch-options">Change</button>
554 </div>
555 </div>
556 `;
557 currentBranchWrapper.append(branchOptions);
558 currentBranchWrapper.addClass("ays-chart-builder-active-options");
559
560 openCloseButton.removeClass("open-branch-options");
561 openCloseButton.addClass("close-branch-options");
562 openCloseIcon.addClass("transform-open-close-icon");
563 }
564
565 this.closeBranchOptions = function ($triggerElement) {
566 var currentBranchWrapper = $triggerElement.parents(".branch-wrapper");
567
568 var openCloseButton = $triggerElement.parent();
569 var openCloseIcon =$triggerElement;
570 if (!$triggerElement.hasClass("fa")) {
571 var openCloseButton = $triggerElement;
572 var openCloseIcon = openCloseButton.children();
573 }
574
575 currentBranchWrapper.find(".ays-chart-org-chart-manual-branch-options-container").remove();
576 currentBranchWrapper.removeClass("ays-chart-builder-active-options");
577
578 openCloseButton.removeClass("close-branch-options");
579 openCloseButton.addClass("open-branch-options");
580 openCloseIcon.removeClass("transform-open-close-icon");
581 }
582
583 this.changeBranchOptions = function ($triggerElement) {
584 const { treeSelector, branchSelector } = self.options;
585
586 var currentBranchWrapper = $triggerElement.parents(".branch-wrapper");
587 var showedBranchTitle = currentBranchWrapper.find(".branch-title");
588 var oldName = showedBranchTitle.text();
589
590 var openCloseButton = currentBranchWrapper.find(".close-branch-options");
591 var currentBranchId = openCloseButton.attr("data-source-id");
592
593 var branchChangedName = $triggerElement.parents(".ays-chart-org-chart-manual-branch-options-container").find("#ays-chart-org-chart-branch-name").val();
594 var branchChangedDescription = $triggerElement.parents(".ays-chart-org-chart-manual-branch-options-container").find("#ays-chart-org-chart-branch-description").val();
595 var branchChangedImage = $triggerElement.parents(".ays-chart-org-chart-manual-branch-options-container").find("#ays-chart-org-chart-branch-image-url").val();
596 var branchChangedTooltip = $triggerElement.parents(".ays-chart-org-chart-manual-branch-options-container").find("#ays-chart-org-chart-branch-tooltip").val();
597 var branchChangedUrl = $triggerElement.parents(".ays-chart-org-chart-manual-branch-options-container").find("#ays-chart-org-chart-branch-url").val();
598
599 currentBranchWrapper.find(".ays-chart-org-chart-name-" + currentBranchId).val(branchChangedName);
600 currentBranchWrapper.find(".ays-chart-org-chart-description-" + currentBranchId).val(branchChangedDescription);
601 currentBranchWrapper.find(".ays-chart-org-chart-image-" + currentBranchId).val(branchChangedImage);
602 currentBranchWrapper.find(".ays-chart-org-chart-tooltip-" + currentBranchId).val(branchChangedTooltip);
603 currentBranchWrapper.find(".ays-chart-org-chart-url-" + currentBranchId).val(branchChangedUrl);
604 $(document).find('.ays-chart-org-chart-parent-name[value="'+oldName+'"]').val(branchChangedName);
605
606 showedBranchTitle.text(branchChangedName);
607 $(treeSelector).detectManualChange();
608 this.closeBranchOptions(openCloseButton);
609 }
610
611 this.updateBranchZIndex = function () {
612 const { treeSelector, branchSelector } = self.options;
613 const $branches = $(`${treeSelector} > ${branchSelector}`);
614 const length = $branches.length;
615
616 $branches.length &&
617 $branches.each(function (index) {
618 $(this).css('z-index', Math.max(1, length - index));
619 });
620 };
621
622 this.initSorting = function () {
623 const {
624 treeSelector,
625 dragHandlerSelector,
626 placeholderName,
627 childrenBusSelector,
628 branchPathSelector,
629 branchSelector,
630 levelPrefix,
631 dataAttributes,
632 maxLevel,
633 } = self.options;
634
635 self.updateBranchZIndex();
636
637 /** Store the current level, for sorting the item after stop dragging. */
638 let currentLevel = 1,
639 originalLevel = 1,
640 childrenBus = null,
641 helperHeight = 0,
642 originalIndex = 0;
643
644 /** Render the branch paths initially. */
645 $(self.options.treeSelector).calculateSiblingDistances();
646
647 /** Update the placeholder branch level by new level. */
648 const updatePlaceholder = (placeholder, level) => {
649 placeholder.updateBranchLevel(level);
650 currentLevel = level;
651 };
652
653 /** Check if we can swap items vertically for branch with children */
654 const canSwapItems = ui => {
655 let offset = ui.helper.offset(),
656 height = offset.top + helperHeight,
657 nextBranch = ui.placeholder.nextBranch(),
658 nextBranchOffset = nextBranch.offset() || 0,
659 nextBranchHeight = nextBranch.outerHeight();
660
661 return height > nextBranchOffset.top + nextBranchHeight / 3;
662 };
663
664 $(treeSelector).sortable({
665 handle: dragHandlerSelector,
666 placeholder: placeholderName,
667 items: '> *',
668 start(_, ui) {
669 /** Synchronize the placeholder level with the item's level. */
670 const level = ui.item.getBranchLevel();
671 ui.placeholder.updateBranchLevel(level);
672 originalIndex = ui.item.index();
673
674 /** Store the original level. */
675 originalLevel = level;
676
677 /** Fill the children bus with the children. */
678 childrenBus = ui.item.find(childrenBusSelector);
679 childrenBus.append(ui.item.next().getDescendants());
680
681 /**
682 * Calculate the placeholder width & height according to the
683 * helper's width & height respectively.
684 */
685 let height = childrenBus.outerHeight();
686 let placeholderMarginTop = ui.placeholder.css('margin-top');
687
688 height += height > 0 ? self.pxToNumber(placeholderMarginTop) : 0;
689 height += ui.helper.outerHeight();
690 helperHeight = height;
691 height -= 2;
692
693 let width = ui.helper.find(branchSelector).outerWidth() - 2;
694 ui.placeholder.css({ height, width });
695
696 const tmp = ui.placeholder.nextBranch();
697 tmp.css('margin-top', self.numberToPx(helperHeight));
698 ui.placeholder.detach();
699 $(this).sortable('refresh');
700 ui.item.after(ui.placeholder);
701 tmp.css('margin-top', 0);
702
703 // Set the current level by the initial item's level.
704 currentLevel = level;
705 $(`${treeSelector} ${branchSelector} ${branchPathSelector}`).hide();
706 },
707 sort(_, ui) {
708 const { depth, maxLevel } = self.options;
709 let treeEdge = self.getTreeEdge(),
710 offset = ui.helper.offset(),
711 currentBranchEdge = offset.left,
712 lowerBound = 1,
713 upperBound = maxLevel;
714
715 /**
716 * Calculate the upper bound. The upper bound would be,
717 * the minimum value between the
718 * (previous branch level + 1) and the maxLevel.
719 */
720 let prevBranch = ui.placeholder.prevBranch();
721 prevBranch = prevBranch[0] === ui.item[0] ? prevBranch.prevBranch() : prevBranch;
722
723 let prevBranchLevel = prevBranch.getBranchLevel();
724 upperBound = Math.min(prevBranchLevel + 1, maxLevel);
725
726 /**
727 * Calculate the lower bound. The lower bound would be,
728 * the maximum value between the
729 * Next Sibling Level and 1
730 */
731 let nextSibling = ui.placeholder.nextSibling(),
732 placeholderLevel = 1;
733
734 if (nextSibling.length) {
735 placeholderLevel = ui.placeholder.getBranchLevel() || 1;
736 } else {
737 /**
738 * If no sibling found then
739 * the placeholder level would be the next branch's level.
740 */
741 let nextBranch = ui.placeholder.nextBranch();
742 placeholderLevel = nextBranch.getBranchLevel() || 1;
743 }
744
745 lowerBound = Math.max(1, placeholderLevel);
746
747 /**
748 * Calculate the position which is the current helper offset left
749 * minus the tree parent's offset left.
750 * Find the changed level by dividing the position by depth value.
751 *
752 * The final valid changed level would be a value
753 * between upper and lower bound inclusive.
754 */
755 let position = Math.max(0, currentBranchEdge - treeEdge);
756 let newLevel = Math.floor(position / depth) + 1;
757 newLevel = Math.max(lowerBound, Math.min(newLevel, upperBound));
758
759 if (canSwapItems(ui)) {
760 let nextBranch = ui.placeholder.nextBranch();
761
762 if (nextBranch.getDescendants().length) {
763 newLevel = nextBranch.getBranchLevel() + 1;
764 }
765
766 nextBranch.after(ui.placeholder);
767 $(this).sortable('refreshPositions');
768 }
769
770 /** Update the placeholder position by the changed level. */
771 updatePlaceholder(ui.placeholder, newLevel);
772 },
773 change(_, ui) {
774 let prevBranch = ui.placeholder.prevBranch();
775
776 prevBranch = prevBranch[0] === ui.item[0] ? prevBranch.prevBranch() : prevBranch;
777
778 /**
779 * After changing branches bound the placeholder to the
780 * changed boundary.
781 */
782 let prevBranchLevel = prevBranch.getBranchLevel() || 1;
783
784 if (prevBranch.length) {
785 ui.placeholder.detach();
786 let children = prevBranch.getDescendants();
787 if (children && children.length) prevBranchLevel += 1;
788 ui.placeholder.updateBranchLevel(prevBranchLevel);
789 prevBranch.after(ui.placeholder);
790 }
791 $(treeSelector).detectManualChange();
792 },
793 stop(_, ui) {
794 $(`${branchSelector}:not(${levelPrefix}-1) ${branchPathSelector}`).show();
795
796 /**
797 * Place the children after the sorted item,
798 * and clear the children bus.
799 */
800 const children = childrenBus.children().insertAfter(ui.item);
801 childrenBus.empty();
802
803 /** Update the item by currently changed level. */
804 ui.item.updateBranchLevel(currentLevel);
805 children.shiftBranchLevel(currentLevel - originalLevel);
806
807 /**
808 * Trigger `sortCompleted` event if the level changed or index changed.
809 * i.e. if the items sorted then trigger the event.
810 */
811 if (currentLevel !== originalLevel || originalIndex !== ui.item.index()) {
812 $(treeSelector).trigger('sortCompleted', [ui]);
813 }
814
815 // Calculate the sibling distance after sorting
816 $(this).calculateSiblingDistances();
817
818 self.updateBranchZIndex();
819
820 /** Update the parent ID and Name after sorting. */
821 const $parent = ui.item.getParent();
822 const $parentName = $parent.find($(".ays-chart-org-chart-name-" + $parent.data(dataAttributes.id))).val();
823 ui.item
824 .data(dataAttributes.parent, $parent.data(dataAttributes.id))
825 .attr(`data-${dataAttributes.parent}`, $parent.data(dataAttributes.id))
826 .find($(".ays-chart-org-chart-parent")).val($parent.data(dataAttributes.id));
827 ui.item.find($(".ays-chart-org-chart-parent-name")).val($parentName);
828 },
829 });
830 };
831 }