2 * ============LICENSE_START=======================================================
\r
4 * ================================================================================
\r
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * ================================================================================
\r
7 * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * you may not use this file except in compliance with the License.
\r
9 * You may obtain a copy of the License at
\r
11 * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * Unless required by applicable law or agreed to in writing, software
\r
14 * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * See the License for the specific language governing permissions and
\r
17 * limitations under the License.
\r
18 * ============LICENSE_END=========================================================
\r
22 * @license Angular UI Tree v2.17.0
\r
23 * (c) 2010-2016. https://github.com/angular-ui-tree/angular-ui-tree
\r
29 angular.module('ui.tree', [])
\r
30 .constant('treeConfig', {
\r
31 treeClass: 'angular-ui-tree',
\r
32 emptyTreeClass: 'angular-ui-tree-empty',
\r
33 hiddenClass: 'angular-ui-tree-hidden',
\r
34 nodesClass: 'angular-ui-tree-nodes',
\r
35 nodeClass: 'angular-ui-tree-node',
\r
36 handleClass: 'angular-ui-tree-handle',
\r
37 placeholderClass: 'angular-ui-tree-placeholder',
\r
38 dragClass: 'angular-ui-tree-drag',
\r
41 defaultCollapsed: false
\r
49 angular.module('ui.tree')
\r
51 .controller('TreeHandleController', ['$scope', '$element',
\r
52 function ($scope, $element) {
\r
53 this.scope = $scope;
\r
55 $scope.$element = $element;
\r
56 $scope.$nodeScope = null;
\r
57 $scope.$type = 'uiTreeHandle';
\r
66 angular.module('ui.tree')
\r
67 .controller('TreeNodeController', ['$scope', '$element',
\r
68 function ($scope, $element) {
\r
69 this.scope = $scope;
\r
71 $scope.$element = $element;
\r
72 $scope.$modelValue = null; // Model value for node;
\r
73 $scope.$parentNodeScope = null; // uiTreeNode Scope of parent node;
\r
74 $scope.$childNodesScope = null; // uiTreeNodes Scope of child nodes.
\r
75 $scope.$parentNodesScope = null; // uiTreeNodes Scope of parent nodes.
\r
76 $scope.$treeScope = null; // uiTree scope
\r
77 $scope.$handleScope = null; // it's handle scope
\r
78 $scope.$type = 'uiTreeNode';
\r
79 $scope.$$allowNodeDrop = false;
\r
80 $scope.collapsed = false;
\r
81 $scope.expandOnHover = false;
\r
83 $scope.init = function (controllersArr) {
\r
84 var treeNodesCtrl = controllersArr[0];
\r
85 $scope.$treeScope = controllersArr[1] ? controllersArr[1].scope : null;
\r
87 // find the scope of it's parent node
\r
88 $scope.$parentNodeScope = treeNodesCtrl.scope.$nodeScope;
\r
89 // modelValue for current node
\r
90 $scope.$modelValue = treeNodesCtrl.scope.$modelValue[$scope.$index];
\r
91 $scope.$parentNodesScope = treeNodesCtrl.scope;
\r
92 treeNodesCtrl.scope.initSubNode($scope); // init sub nodes
\r
94 $element.on('$destroy', function () {
\r
95 treeNodesCtrl.scope.destroySubNode($scope); // destroy sub nodes
\r
99 $scope.index = function () {
\r
100 return $scope.$parentNodesScope.$modelValue.indexOf($scope.$modelValue);
\r
103 $scope.dragEnabled = function () {
\r
104 return !($scope.$treeScope && !$scope.$treeScope.dragEnabled);
\r
107 $scope.isSibling = function (targetNode) {
\r
108 return $scope.$parentNodesScope == targetNode.$parentNodesScope;
\r
111 $scope.isChild = function (targetNode) {
\r
112 var nodes = $scope.childNodes();
\r
113 return nodes && nodes.indexOf(targetNode) > -1;
\r
116 $scope.prev = function () {
\r
117 var index = $scope.index();
\r
119 return $scope.siblings()[index - 1];
\r
124 $scope.siblings = function () {
\r
125 return $scope.$parentNodesScope.childNodes();
\r
128 $scope.childNodesCount = function () {
\r
129 return $scope.childNodes() ? $scope.childNodes().length : 0;
\r
132 $scope.hasChild = function () {
\r
133 return $scope.childNodesCount() > 0;
\r
136 $scope.childNodes = function () {
\r
137 return $scope.$childNodesScope && $scope.$childNodesScope.$modelValue ?
\r
138 $scope.$childNodesScope.childNodes() :
\r
142 $scope.accept = function (sourceNode, destIndex) {
\r
143 return $scope.$childNodesScope &&
\r
144 $scope.$childNodesScope.$modelValue &&
\r
145 $scope.$childNodesScope.accept(sourceNode, destIndex);
\r
148 $scope.remove = function () {
\r
149 return $scope.$parentNodesScope.removeNode($scope);
\r
152 $scope.toggle = function () {
\r
153 $scope.collapsed = !$scope.collapsed;
\r
154 $scope.$treeScope.$callbacks.toggle($scope.collapsed, $scope);
\r
157 $scope.collapse = function () {
\r
158 $scope.collapsed = true;
\r
161 $scope.expand = function () {
\r
162 $scope.collapsed = false;
\r
165 $scope.depth = function () {
\r
166 var parentNode = $scope.$parentNodeScope;
\r
168 return parentNode.depth() + 1;
\r
174 * Returns the depth of the deepest subtree under this node
\r
175 * @param scope a TreeNodesController scope object
\r
176 * @returns Depth of all nodes *beneath* this node. If scope belongs to a leaf node, the
\r
177 * result is 0 (it has no subtree).
\r
179 function countSubTreeDepth(scope) {
\r
180 var thisLevelDepth = 0,
\r
181 childNodes = scope.childNodes(),
\r
185 if (!childNodes || childNodes.length === 0) {
\r
188 for (i = childNodes.length - 1; i >= 0 ; i--) {
\r
189 childNode = childNodes[i],
\r
190 childDepth = 1 + countSubTreeDepth(childNode);
\r
191 thisLevelDepth = Math.max(thisLevelDepth, childDepth);
\r
193 return thisLevelDepth;
\r
196 $scope.maxSubDepth = function () {
\r
197 return $scope.$childNodesScope ? countSubTreeDepth($scope.$childNodesScope) : 0;
\r
206 angular.module('ui.tree')
\r
208 .controller('TreeNodesController', ['$scope', '$element',
\r
209 function ($scope, $element) {
\r
210 this.scope = $scope;
\r
212 $scope.$element = $element;
\r
213 $scope.$modelValue = null;
\r
214 $scope.$nodeScope = null; // the scope of node which the nodes belongs to
\r
215 $scope.$treeScope = null;
\r
216 $scope.$type = 'uiTreeNodes';
\r
217 $scope.$nodesMap = {};
\r
219 $scope.nodropEnabled = false;
\r
220 $scope.maxDepth = 0;
\r
221 $scope.cloneEnabled = false;
\r
223 $scope.initSubNode = function (subNode) {
\r
224 if (!subNode.$modelValue) {
\r
227 $scope.$nodesMap[subNode.$modelValue.$$hashKey] = subNode;
\r
230 $scope.destroySubNode = function (subNode) {
\r
231 if (!subNode.$modelValue) {
\r
234 $scope.$nodesMap[subNode.$modelValue.$$hashKey] = null;
\r
237 $scope.accept = function (sourceNode, destIndex) {
\r
238 return $scope.$treeScope.$callbacks.accept(sourceNode, $scope, destIndex);
\r
241 $scope.beforeDrag = function (sourceNode) {
\r
242 return $scope.$treeScope.$callbacks.beforeDrag(sourceNode);
\r
245 $scope.isParent = function (node) {
\r
246 return node.$parentNodesScope == $scope;
\r
249 $scope.hasChild = function () {
\r
250 return $scope.$modelValue.length > 0;
\r
253 $scope.safeApply = function (fn) {
\r
254 var phase = this.$root.$$phase;
\r
255 if (phase == '$apply' || phase == '$digest') {
\r
256 if (fn && (typeof (fn) === 'function')) {
\r
264 $scope.removeNode = function (node) {
\r
265 var index = $scope.$modelValue.indexOf(node.$modelValue);
\r
267 $scope.safeApply(function () {
\r
268 $scope.$modelValue.splice(index, 1)[0];
\r
270 return $scope.$treeScope.$callbacks.removed(node);
\r
275 $scope.insertNode = function (index, nodeData) {
\r
276 $scope.safeApply(function () {
\r
277 $scope.$modelValue.splice(index, 0, nodeData);
\r
281 $scope.childNodes = function () {
\r
283 if ($scope.$modelValue) {
\r
284 for (i = 0; i < $scope.$modelValue.length; i++) {
\r
285 nodes.push($scope.$nodesMap[$scope.$modelValue[i].$$hashKey]);
\r
291 $scope.depth = function () {
\r
292 if ($scope.$nodeScope) {
\r
293 return $scope.$nodeScope.depth();
\r
295 return 0; // if it has no $nodeScope, it's root
\r
298 // check if depth limit has reached
\r
299 $scope.outOfDepth = function (sourceNode) {
\r
300 var maxDepth = $scope.maxDepth || $scope.$treeScope.maxDepth;
\r
301 if (maxDepth > 0) {
\r
302 return $scope.depth() + sourceNode.maxSubDepth() + 1 > maxDepth;
\r
314 angular.module('ui.tree')
\r
316 .controller('TreeController', ['$scope', '$element',
\r
317 function ($scope, $element) {
\r
318 this.scope = $scope;
\r
320 $scope.$element = $element;
\r
321 $scope.$nodesScope = null; // root nodes
\r
322 $scope.$type = 'uiTree';
\r
323 $scope.$emptyElm = null;
\r
324 $scope.$callbacks = null;
\r
326 $scope.dragEnabled = true;
\r
327 $scope.emptyPlaceholderEnabled = true;
\r
328 $scope.maxDepth = 0;
\r
329 $scope.dragDelay = 0;
\r
330 $scope.cloneEnabled = false;
\r
331 $scope.nodropEnabled = false;
\r
333 // Check if it's a empty tree
\r
334 $scope.isEmpty = function () {
\r
335 return ($scope.$nodesScope && $scope.$nodesScope.$modelValue
\r
336 && $scope.$nodesScope.$modelValue.length === 0);
\r
339 // add placeholder to empty tree
\r
340 $scope.place = function (placeElm) {
\r
341 $scope.$nodesScope.$element.append(placeElm);
\r
342 $scope.$emptyElm.remove();
\r
345 this.resetEmptyElement = function () {
\r
346 if ((!$scope.$nodesScope.$modelValue || $scope.$nodesScope.$modelValue.length === 0) &&
\r
347 $scope.emptyPlaceholderEnabled) {
\r
348 $element.append($scope.$emptyElm);
\r
350 $scope.$emptyElm.remove();
\r
354 $scope.resetEmptyElement = this.resetEmptyElement;
\r
362 angular.module('ui.tree')
\r
363 .directive('uiTree', ['treeConfig', '$window',
\r
364 function (treeConfig, $window) {
\r
368 controller: 'TreeController',
\r
369 link: function (scope, element, attrs, ctrl) {
\r
379 angular.extend(config, treeConfig);
\r
380 if (config.treeClass) {
\r
381 element.addClass(config.treeClass);
\r
384 if (element.prop('tagName').toLowerCase() === 'table') {
\r
385 scope.$emptyElm = angular.element($window.document.createElement('tr'));
\r
386 $trElm = element.find('tr');
\r
387 // If we can find a tr, then we can use its td children as the empty element colspan.
\r
388 if ($trElm.length > 0) {
\r
389 emptyElmColspan = angular.element($trElm).children().length;
\r
391 // If not, by setting a huge colspan we make sure it takes full width.
\r
392 emptyElmColspan = 1000000;
\r
394 tdElm = angular.element($window.document.createElement('td'))
\r
395 .attr('colspan', emptyElmColspan);
\r
396 scope.$emptyElm.append(tdElm);
\r
398 scope.$emptyElm = angular.element($window.document.createElement('div'));
\r
401 if (config.emptyTreeClass) {
\r
402 scope.$emptyElm.addClass(config.emptyTreeClass);
\r
405 scope.$watch('$nodesScope.$modelValue.length', function (val) {
\r
406 if (!angular.isNumber(val)) {
\r
410 ctrl.resetEmptyElement();
\r
413 scope.$watch(attrs.dragEnabled, function (val) {
\r
414 if ((typeof val) == 'boolean') {
\r
415 scope.dragEnabled = val;
\r
419 scope.$watch(attrs.emptyPlaceholderEnabled, function (val) {
\r
420 if ((typeof val) == 'boolean') {
\r
421 scope.emptyPlaceholderEnabled = val;
\r
422 ctrl.resetEmptyElement();
\r
426 scope.$watch(attrs.nodropEnabled, function (val) {
\r
427 if ((typeof val) == 'boolean') {
\r
428 scope.nodropEnabled = val;
\r
432 scope.$watch(attrs.cloneEnabled, function (val) {
\r
433 if ((typeof val) == 'boolean') {
\r
434 scope.cloneEnabled = val;
\r
438 scope.$watch(attrs.maxDepth, function (val) {
\r
439 if ((typeof val) == 'number') {
\r
440 scope.maxDepth = val;
\r
444 scope.$watch(attrs.dragDelay, function (val) {
\r
445 if ((typeof val) == 'number') {
\r
446 scope.dragDelay = val;
\r
451 * Callback checks if the destination node can accept the dragged node.
\r
452 * By default, ui-tree will check that 'data-nodrop-enabled' is not set for the
\r
453 * destination ui-tree-nodes, and that the 'max-depth' attribute will not be exceeded
\r
454 * if it is set on the ui-tree or ui-tree-nodes.
\r
455 * This callback can be overridden, but callers must manually enforce nodrop and max-depth
\r
456 * themselves if they need those to be enforced.
\r
457 * @param sourceNodeScope Scope of the ui-tree-node being dragged
\r
458 * @param destNodesScope Scope of the ui-tree-nodes where the node is hovering
\r
459 * @param destIndex Index in the destination nodes array where the source node will drop
\r
460 * @returns {boolean} True if the node is permitted to be dropped here
\r
462 callbacks.accept = function (sourceNodeScope, destNodesScope, destIndex) {
\r
463 return !(destNodesScope.nodropEnabled || destNodesScope.$treeScope.nodropEnabled || destNodesScope.outOfDepth(sourceNodeScope));
\r
466 callbacks.beforeDrag = function (sourceNodeScope) {
\r
470 callbacks.expandTimeoutStart = function()
\r
475 callbacks.expandTimeoutCancel = function()
\r
480 callbacks.expandTimeoutEnd = function()
\r
485 callbacks.removed = function (node) {
\r
490 * Callback is fired when a node is successfully dropped in a new location
\r
493 callbacks.dropped = function (event) {
\r
498 * Callback is fired each time the user starts dragging a node
\r
501 callbacks.dragStart = function (event) {
\r
506 * Callback is fired each time a dragged node is moved with the mouse/touch.
\r
509 callbacks.dragMove = function (event) {
\r
514 * Callback is fired when the tree exits drag mode. If the user dropped a node, the drop may have been
\r
515 * accepted or reverted.
\r
518 callbacks.dragStop = function (event) {
\r
523 * Callback is fired when a user drops a node (but prior to processing the drop action)
\r
524 * beforeDrop can return a Promise, truthy, or falsy (returning nothing is falsy).
\r
525 * If it returns falsy, or a resolve Promise, the node move is accepted
\r
526 * If it returns truthy, or a rejected Promise, the node move is reverted
\r
528 * @returns {Boolean|Promise} Truthy (or rejected Promise) to cancel node move; falsy (or resolved promise)
\r
530 callbacks.beforeDrop = function (event) {
\r
535 * Callback is fired when a user toggles node (but after processing the toggle action)
\r
536 * @param sourceNodeScope
\r
539 callbacks.toggle = function (collapsed, sourceNodeScope) {
\r
543 scope.$watch(attrs.uiTree, function (newVal, oldVal) {
\r
544 angular.forEach(newVal, function (value, key) {
\r
545 if (callbacks[key]) {
\r
546 if (typeof value === 'function') {
\r
547 callbacks[key] = value;
\r
552 scope.$callbacks = callbacks;
\r
565 angular.module('ui.tree')
\r
566 .directive('uiTreeHandle', ['treeConfig',
\r
567 function (treeConfig) {
\r
569 require: '^uiTreeNode',
\r
572 controller: 'TreeHandleController',
\r
573 link: function (scope, element, attrs, treeNodeCtrl) {
\r
575 angular.extend(config, treeConfig);
\r
576 if (config.handleClass) {
\r
577 element.addClass(config.handleClass);
\r
579 // connect with the tree node.
\r
580 if (scope != treeNodeCtrl.scope) {
\r
581 scope.$nodeScope = treeNodeCtrl.scope;
\r
582 treeNodeCtrl.scope.$handleScope = scope;
\r
593 angular.module('ui.tree')
\r
595 .directive('uiTreeNode', ['treeConfig', 'UiTreeHelper', '$window', '$document', '$timeout', '$q',
\r
596 function (treeConfig, UiTreeHelper, $window, $document, $timeout, $q) {
\r
598 require: ['^uiTreeNodes', '^uiTree'],
\r
600 controller: 'TreeNodeController',
\r
601 link: function (scope, element, attrs, controllersArr) {
\r
602 // todo startPos is unused
\r
604 hasTouch = 'ontouchstart' in window,
\r
605 startPos, firstMoving, dragInfo, pos,
\r
606 placeElm, hiddenPlaceElm, dragElm,
\r
608 elements, // As a parameter for callbacks
\r
609 dragDelaying = true,
\r
610 dragStarted = false,
\r
612 body = document.body,
\r
613 html = document.documentElement,
\r
625 bindDragStartEvents,
\r
626 bindDragMoveEvents,
\r
627 unbindDragMoveEvents,
\r
633 angular.extend(config, treeConfig);
\r
634 if (config.nodeClass) {
\r
635 element.addClass(config.nodeClass);
\r
637 scope.init(controllersArr);
\r
639 scope.collapsed = !!UiTreeHelper.getNodeAttribute(scope, 'collapsed') || treeConfig.defaultCollapsed;
\r
640 scope.expandOnHover = !!UiTreeHelper.getNodeAttribute(scope, 'expandOnHover');
\r
641 scope.sourceOnly = scope.nodropEnabled || scope.$treeScope.nodropEnabled;
\r
643 scope.$watch(attrs.collapsed, function (val) {
\r
644 if ((typeof val) == 'boolean') {
\r
645 scope.collapsed = val;
\r
649 scope.$watch('collapsed', function (val) {
\r
650 UiTreeHelper.setNodeAttribute(scope, 'collapsed', val);
\r
651 attrs.$set('collapsed', val);
\r
654 scope.$watch(attrs.expandOnHover, function(val) {
\r
655 if ((typeof val) == 'boolean') {
\r
656 scope.expandOnHover = val;
\r
660 scope.$watch('expandOnHover', function (val) {
\r
661 UiTreeHelper.setNodeAttribute(scope, 'expandOnHover', val);
\r
662 attrs.$set('expandOnHover', val);
\r
665 scope.$on('angular-ui-tree:collapse-all', function () {
\r
666 scope.collapsed = true;
\r
669 scope.$on('angular-ui-tree:expand-all', function () {
\r
670 scope.collapsed = false;
\r
674 * Called when the user has grabbed a node and started dragging it
\r
677 dragStart = function (e) {
\r
678 // disable right click
\r
679 if (!hasTouch && (e.button === 2 || e.which === 3)) {
\r
683 // event has already fired in other scope
\r
684 if (e.uiTreeDragging || (e.originalEvent && e.originalEvent.uiTreeDragging)) {
\r
688 // the node being dragged
\r
689 var eventElm = angular.element(e.target),
\r
690 isHandleChild, cloneElm, eventElmTagName, tagName,
\r
691 eventObj, tdElm, hStyle,
\r
695 // if the target element is a child element of a ui-tree-handle,
\r
696 // use the containing handle element as target element
\r
697 isHandleChild = UiTreeHelper.treeNodeHandlerContainerOfElement(eventElm);
\r
698 if (isHandleChild) {
\r
699 eventElm = angular.element(isHandleChild);
\r
702 cloneElm = element.clone();
\r
703 isTreeNode = UiTreeHelper.elementIsTreeNode(eventElm);
\r
704 isTreeNodeHandle = UiTreeHelper.elementIsTreeNodeHandle(eventElm);
\r
706 if (!isTreeNode && !isTreeNodeHandle) {
\r
710 if (isTreeNode && UiTreeHelper.elementContainsTreeNodeHandler(eventElm)) {
\r
714 eventElmTagName = eventElm.prop('tagName').toLowerCase();
\r
715 if (eventElmTagName == 'input' ||
\r
716 eventElmTagName == 'textarea' ||
\r
717 eventElmTagName == 'button' ||
\r
718 eventElmTagName == 'select') { // if it's a input or button, ignore it
\r
722 // check if it or it's parents has a 'data-nodrag' attribute
\r
723 el = angular.element(e.target);
\r
724 while (el && el[0] && el[0] !== element) {
\r
725 if (UiTreeHelper.nodrag(el)) { // if the node mark as `nodrag`, DONOT drag it.
\r
731 if (!scope.beforeDrag(scope)) {
\r
735 e.uiTreeDragging = true; // stop event bubbling
\r
736 if (e.originalEvent) {
\r
737 e.originalEvent.uiTreeDragging = true;
\r
739 e.preventDefault();
\r
740 eventObj = UiTreeHelper.eventObj(e);
\r
742 firstMoving = true;
\r
743 dragInfo = UiTreeHelper.dragInfo(scope);
\r
745 tagName = element.prop('tagName');
\r
747 if (tagName.toLowerCase() === 'tr') {
\r
748 placeElm = angular.element($window.document.createElement(tagName));
\r
749 tdElm = angular.element($window.document.createElement('td'))
\r
750 .addClass(config.placeholderClass)
\r
751 .attr('colspan', element[0].children.length);
\r
752 placeElm.append(tdElm);
\r
754 placeElm = angular.element($window.document.createElement(tagName))
\r
755 .addClass(config.placeholderClass);
\r
757 hiddenPlaceElm = angular.element($window.document.createElement(tagName));
\r
758 if (config.hiddenClass) {
\r
759 hiddenPlaceElm.addClass(config.hiddenClass);
\r
762 pos = UiTreeHelper.positionStarted(eventObj, element);
\r
763 placeElm.css('height', UiTreeHelper.height(element) + 'px');
\r
765 dragElm = angular.element($window.document.createElement(scope.$parentNodesScope.$element.prop('tagName')))
\r
766 .addClass(scope.$parentNodesScope.$element.attr('class')).addClass(config.dragClass);
\r
767 dragElm.css('width', UiTreeHelper.width(element) + 'px');
\r
768 dragElm.css('z-index', 9999);
\r
770 // Prevents cursor to change rapidly in Opera 12.16 and IE when dragging an element
\r
771 hStyle = (element[0].querySelector('.angular-ui-tree-handle') || element[0]).currentStyle;
\r
773 document.body.setAttribute('ui-tree-cursor', $document.find('body').css('cursor') || '');
\r
774 $document.find('body').css({'cursor': hStyle.cursor + '!important'});
\r
777 if (scope.sourceOnly) {
\r
778 placeElm.css('display', 'none');
\r
780 element.after(placeElm);
\r
781 element.after(hiddenPlaceElm);
\r
782 if (dragInfo.isClone() && scope.sourceOnly) {
\r
783 dragElm.append(cloneElm);
\r
785 dragElm.append(element);
\r
788 $document.find('body').append(dragElm);
\r
791 'left': eventObj.pageX - pos.offsetX + 'px',
\r
792 'top': eventObj.pageY - pos.offsetY + 'px'
\r
795 placeholder: placeElm,
\r
799 bindDragMoveEvents();
\r
800 // Fire dragStart callback
\r
801 scope.$apply(function () {
\r
802 scope.$treeScope.$callbacks.dragStart(dragInfo.eventArgs(elements, pos));
\r
805 document_height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
\r
806 document_width = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
\r
809 dragMove = function (e) {
\r
810 var eventObj = UiTreeHelper.eventObj(e),
\r
830 e.preventDefault();
\r
832 if ($window.getSelection) {
\r
833 $window.getSelection().removeAllRanges();
\r
834 } else if ($window.document.selection) {
\r
835 $window.document.selection.empty();
\r
838 leftElmPos = eventObj.pageX - pos.offsetX;
\r
839 topElmPos = eventObj.pageY - pos.offsetY;
\r
841 //dragElm can't leave the screen on the left
\r
842 if (leftElmPos < 0) {
\r
846 //dragElm can't leave the screen on the top
\r
847 if (topElmPos < 0) {
\r
851 //dragElm can't leave the screen on the bottom
\r
852 if ((topElmPos + 10) > document_height) {
\r
853 topElmPos = document_height - 10;
\r
856 //dragElm can't leave the screen on the right
\r
857 if ((leftElmPos + 10) > document_width) {
\r
858 leftElmPos = document_width - 10;
\r
862 'left': leftElmPos + 'px',
\r
863 'top': topElmPos + 'px'
\r
866 top_scroll = window.pageYOffset || $window.document.documentElement.scrollTop;
\r
867 bottom_scroll = top_scroll + (window.innerHeight || $window.document.clientHeight || $window.document.clientHeight);
\r
869 // to scroll down if cursor y-position is greater than the bottom position the vertical scroll
\r
870 if (bottom_scroll < eventObj.pageY && bottom_scroll < document_height) {
\r
871 scrollDownBy = Math.min(document_height - bottom_scroll, 10);
\r
872 window.scrollBy(0, scrollDownBy);
\r
875 // to scroll top if cursor y-position is less than the top position the vertical scroll
\r
876 if (top_scroll > eventObj.pageY) {
\r
877 window.scrollBy(0, -10);
\r
880 UiTreeHelper.positionMoved(e, pos, firstMoving);
\r
882 firstMoving = false;
\r
886 // check if add it as a child node first
\r
887 // todo decrease is unused
\r
888 decrease = (UiTreeHelper.offset(dragElm).left - UiTreeHelper.offset(placeElm).left) >= config.threshold;
\r
890 targetX = eventObj.pageX - ($window.pageXOffset ||
\r
891 $window.document.body.scrollLeft ||
\r
892 $window.document.documentElement.scrollLeft) -
\r
893 ($window.document.documentElement.clientLeft || 0);
\r
895 targetY = eventObj.pageY - ($window.pageYOffset ||
\r
896 $window.document.body.scrollTop ||
\r
897 $window.document.documentElement.scrollTop) -
\r
898 ($window.document.documentElement.clientTop || 0);
\r
900 // Select the drag target. Because IE does not support CSS 'pointer-events: none', it will always
\r
901 // pick the drag element itself as the target. To prevent this, we hide the drag element while
\r
902 // selecting the target.
\r
903 if (angular.isFunction(dragElm.hide)) {
\r
906 displayElm = dragElm[0].style.display;
\r
907 dragElm[0].style.display = 'none';
\r
910 // when using elementFromPoint() inside an iframe, you have to call
\r
911 // elementFromPoint() twice to make sure IE8 returns the correct value
\r
912 $window.document.elementFromPoint(targetX, targetY);
\r
914 targetElm = angular.element($window.document.elementFromPoint(targetX, targetY));
\r
916 // if the target element is a child element of a ui-tree-handle,
\r
917 // use the containing handle element as target element
\r
918 isHandleChild = UiTreeHelper.treeNodeHandlerContainerOfElement(targetElm);
\r
919 if (isHandleChild) {
\r
920 targetElm = angular.element(isHandleChild);
\r
923 if (angular.isFunction(dragElm.show)) {
\r
926 dragElm[0].style.display = displayElm;
\r
929 outOfBounds = !UiTreeHelper.elementIsTreeNodeHandle(targetElm) &&
\r
930 !UiTreeHelper.elementIsTreeNode(targetElm) &&
\r
931 !UiTreeHelper.elementIsTreeNodes(targetElm) &&
\r
932 !UiTreeHelper.elementIsTree(targetElm) &&
\r
933 !UiTreeHelper.elementIsPlaceholder(targetElm);
\r
935 // Detect out of bounds condition, update drop target display, and prevent drop
\r
938 // Remove the placeholder
\r
941 // If the target was an empty tree, replace the empty element placeholder
\r
943 treeScope.resetEmptyElement();
\r
949 if (pos.dirAx && pos.distAxX >= config.levelThreshold) {
\r
952 // increase horizontal level if previous sibling exists and is not collapsed
\r
953 if (pos.distX > 0) {
\r
954 prev = dragInfo.prev();
\r
955 if (prev && !prev.collapsed
\r
956 && prev.accept(scope, prev.childNodesCount())) {
\r
957 prev.$childNodesScope.$element.append(placeElm);
\r
958 dragInfo.moveTo(prev.$childNodesScope, prev.childNodes(), prev.childNodesCount());
\r
962 // decrease horizontal level
\r
963 if (pos.distX < 0) {
\r
964 // we can't decrease a level if an item preceeds the current one
\r
965 next = dragInfo.next();
\r
967 target = dragInfo.parentNode(); // As a sibling of it's parent node
\r
969 && target.$parentNodesScope.accept(scope, target.index() + 1)) {
\r
970 target.$element.after(placeElm);
\r
971 dragInfo.moveTo(target.$parentNodesScope, target.siblings(), target.index() + 1);
\r
979 if (UiTreeHelper.elementIsTree(targetElm)) {
\r
980 targetNode = targetElm.controller('uiTree').scope;
\r
981 } else if (UiTreeHelper.elementIsTreeNodeHandle(targetElm)) {
\r
982 targetNode = targetElm.controller('uiTreeHandle').scope;
\r
983 } else if (UiTreeHelper.elementIsTreeNode(targetElm)) {
\r
984 targetNode = targetElm.controller('uiTreeNode').scope;
\r
985 } else if (UiTreeHelper.elementIsTreeNodes(targetElm)) {
\r
986 targetNode = targetElm.controller('uiTreeNodes').scope;
\r
987 } else if (UiTreeHelper.elementIsPlaceholder(targetElm)) {
\r
988 targetNode = targetElm.controller('uiTreeNodes').scope;
\r
989 } else if (targetElm.controller('uiTreeNode')) {
\r
990 // is a child element of a node
\r
991 targetNode = targetElm.controller('uiTreeNode').scope;
\r
994 // check it's new position
\r
1000 // Show the placeholder if it was hidden for nodrop-enabled and this is a new tree
\r
1001 if (targetNode.$treeScope && !targetNode.$parent.nodropEnabled && !targetNode.$treeScope.nodropEnabled) {
\r
1002 placeElm.css('display', '');
\r
1005 if (targetNode.$type == 'uiTree' && targetNode.dragEnabled) {
\r
1006 isEmpty = targetNode.isEmpty(); // Check if it's empty tree
\r
1009 if (targetNode.$type == 'uiTreeHandle') {
\r
1010 targetNode = targetNode.$nodeScope;
\r
1013 if (targetNode.$type != 'uiTreeNode'
\r
1014 && !isEmpty) { // Check if it is a uiTreeNode or it's an empty tree
\r
1018 // if placeholder move from empty tree, reset it.
\r
1019 if (treeScope && placeElm.parent()[0] != treeScope.$element[0]) {
\r
1020 treeScope.resetEmptyElement();
\r
1024 if (isEmpty) { // it's an empty tree
\r
1025 treeScope = targetNode;
\r
1026 if (targetNode.$nodesScope.accept(scope, 0)) {
\r
1027 targetNode.place(placeElm);
\r
1028 dragInfo.moveTo(targetNode.$nodesScope, targetNode.$nodesScope.childNodes(), 0);
\r
1030 } else if (targetNode.dragEnabled()) { // drag enabled
\r
1031 if (angular.isDefined(scope.expandTimeoutOn) && scope.expandTimeoutOn !== targetNode.id) {
\r
1032 $timeout.cancel(scope.expandTimeout);
\r
1033 delete scope.expandTimeout;
\r
1034 delete scope.expandTimeoutOn;
\r
1036 scope.$callbacks.expandTimeoutCancel();
\r
1039 if (targetNode.collapsed) {
\r
1040 if (scope.expandOnHover === true || (angular.isNumber(scope.expandOnHover) && scope.expandOnHover === 0)) {
\r
1041 targetNode.collapsed = false;
\r
1042 } else if (scope.expandOnHover !== false && angular.isNumber(scope.expandOnHover) && scope.expandOnHover > 0) {
\r
1043 if (angular.isUndefined(scope.expandTimeoutOn)) {
\r
1044 scope.expandTimeoutOn = targetNode.$id;
\r
1046 scope.$callbacks.expandTimeoutStart();
\r
1047 scope.expandTimeout = $timeout(function()
\r
1049 scope.$callbacks.expandTimeoutEnd();
\r
1050 targetNode.collapsed = false;
\r
1051 }, scope.expandOnHover);
\r
1056 targetElm = targetNode.$element; // Get the element of ui-tree-node
\r
1057 targetOffset = UiTreeHelper.offset(targetElm);
\r
1058 targetBefore = targetNode.horizontal ? eventObj.pageX < (targetOffset.left + UiTreeHelper.width(targetElm) / 2)
\r
1059 : eventObj.pageY < (targetOffset.top + UiTreeHelper.height(targetElm) / 2);
\r
1061 if (targetNode.$parentNodesScope.accept(scope, targetNode.index())) {
\r
1062 if (targetBefore) {
\r
1063 targetElm[0].parentNode.insertBefore(placeElm[0], targetElm[0]);
\r
1064 dragInfo.moveTo(targetNode.$parentNodesScope, targetNode.siblings(), targetNode.index());
\r
1066 targetElm.after(placeElm);
\r
1067 dragInfo.moveTo(targetNode.$parentNodesScope, targetNode.siblings(), targetNode.index() + 1);
\r
1069 } else if (!targetBefore && targetNode.accept(scope, targetNode.childNodesCount())) { // we have to check if it can add the dragging node as a child
\r
1070 targetNode.$childNodesScope.$element.append(placeElm);
\r
1071 dragInfo.moveTo(targetNode.$childNodesScope, targetNode.childNodes(), targetNode.childNodesCount());
\r
1073 outOfBounds = true;
\r
1078 scope.$apply(function () {
\r
1079 scope.$treeScope.$callbacks.dragMove(dragInfo.eventArgs(elements, pos));
\r
1084 dragEnd = function (e) {
\r
1085 var dragEventArgs = dragInfo.eventArgs(elements, pos);
\r
1086 e.preventDefault();
\r
1087 unbindDragMoveEvents();
\r
1089 $timeout.cancel(scope.expandTimeout);
\r
1091 scope.$treeScope.$apply(function () {
\r
1092 $q.when(scope.$treeScope.$callbacks.beforeDrop(dragEventArgs))
\r
1093 // promise resolved (or callback didn't return false)
\r
1094 .then(function (allowDrop) {
\r
1095 if (allowDrop !== false && scope.$$allowNodeDrop && !outOfBounds) { // node drop accepted)
\r
1097 // fire the dropped callback only if the move was successful
\r
1098 scope.$treeScope.$callbacks.dropped(dragEventArgs);
\r
1099 } else { // drop canceled - revert the node to its original position
\r
1100 bindDragStartEvents();
\r
1103 // promise rejected - revert the node to its original position
\r
1104 .catch(function () {
\r
1105 bindDragStartEvents();
\r
1107 .finally(function () {
\r
1108 hiddenPlaceElm.replaceWith(scope.$element);
\r
1109 placeElm.remove();
\r
1111 if (dragElm) { // drag element is attached to the mouse pointer
\r
1115 scope.$treeScope.$callbacks.dragStop(dragEventArgs);
\r
1116 scope.$$allowNodeDrop = false;
\r
1119 // Restore cursor in Opera 12.16 and IE
\r
1120 var oldCur = document.body.getAttribute('ui-tree-cursor');
\r
1121 if (oldCur !== null) {
\r
1122 $document.find('body').css({'cursor': oldCur});
\r
1123 document.body.removeAttribute('ui-tree-cursor');
\r
1129 dragStartEvent = function (e) {
\r
1130 if (scope.dragEnabled()) {
\r
1135 dragMoveEvent = function (e) {
\r
1139 dragEndEvent = function (e) {
\r
1140 scope.$$allowNodeDrop = true;
\r
1144 dragCancelEvent = function (e) {
\r
1148 dragDelay = (function () {
\r
1152 exec: function (fn, ms) {
\r
1157 to = $timeout(fn, ms);
\r
1159 cancel: function () {
\r
1160 $timeout.cancel(to);
\r
1166 * Binds the mouse/touch events to enable drag start for this node
\r
1168 bindDragStartEvents = function () {
\r
1169 element.bind('touchstart mousedown', function (e) {
\r
1170 dragDelay.exec(function () {
\r
1171 dragStartEvent(e);
\r
1172 }, scope.dragDelay || 0);
\r
1174 element.bind('touchend touchcancel mouseup', function () {
\r
1175 dragDelay.cancel();
\r
1178 bindDragStartEvents();
\r
1181 * Binds mouse/touch events that handle moving/dropping this dragged node
\r
1183 bindDragMoveEvents = function () {
\r
1184 angular.element($document).bind('touchend', dragEndEvent);
\r
1185 angular.element($document).bind('touchcancel', dragEndEvent);
\r
1186 angular.element($document).bind('touchmove', dragMoveEvent);
\r
1187 angular.element($document).bind('mouseup', dragEndEvent);
\r
1188 angular.element($document).bind('mousemove', dragMoveEvent);
\r
1189 angular.element($document).bind('mouseleave', dragCancelEvent);
\r
1193 * Unbinds mouse/touch events that handle moving/dropping this dragged node
\r
1195 unbindDragMoveEvents = function () {
\r
1196 angular.element($document).unbind('touchend', dragEndEvent);
\r
1197 angular.element($document).unbind('touchcancel', dragEndEvent);
\r
1198 angular.element($document).unbind('touchmove', dragMoveEvent);
\r
1199 angular.element($document).unbind('mouseup', dragEndEvent);
\r
1200 angular.element($document).unbind('mousemove', dragMoveEvent);
\r
1201 angular.element($document).unbind('mouseleave', dragCancelEvent);
\r
1204 keydownHandler = function (e) {
\r
1205 if (e.keyCode == 27) {
\r
1206 scope.$$allowNodeDrop = false;
\r
1211 angular.element($window.document).bind('keydown', keydownHandler);
\r
1213 //unbind handler that retains scope
\r
1214 scope.$on('$destroy', function () {
\r
1215 angular.element($window.document).unbind('keydown', keydownHandler);
\r
1227 angular.module('ui.tree')
\r
1228 .directive('uiTreeNodes', ['treeConfig', '$window',
\r
1229 function (treeConfig) {
\r
1231 require: ['ngModel', '?^uiTreeNode', '^uiTree'],
\r
1234 controller: 'TreeNodesController',
\r
1235 link: function (scope, element, attrs, controllersArr) {
\r
1238 ngModel = controllersArr[0],
\r
1239 treeNodeCtrl = controllersArr[1],
\r
1240 treeCtrl = controllersArr[2];
\r
1242 angular.extend(config, treeConfig);
\r
1243 if (config.nodesClass) {
\r
1244 element.addClass(config.nodesClass);
\r
1247 if (treeNodeCtrl) {
\r
1248 treeNodeCtrl.scope.$childNodesScope = scope;
\r
1249 scope.$nodeScope = treeNodeCtrl.scope;
\r
1251 // find the root nodes if there is no parent node and have a parent ui-tree
\r
1252 treeCtrl.scope.$nodesScope = scope;
\r
1254 scope.$treeScope = treeCtrl.scope;
\r
1257 ngModel.$render = function () {
\r
1258 scope.$modelValue = ngModel.$modelValue;
\r
1262 scope.$watch(function () {
\r
1263 return attrs.maxDepth;
\r
1264 }, function (val) {
\r
1265 if ((typeof val) == 'number') {
\r
1266 scope.maxDepth = val;
\r
1270 scope.$watch(function () {
\r
1271 return attrs.nodropEnabled;
\r
1272 }, function (newVal) {
\r
1273 if ((typeof newVal) != 'undefined') {
\r
1274 scope.nodropEnabled = true;
\r
1278 attrs.$observe('horizontal', function (val) {
\r
1279 scope.horizontal = ((typeof val) != 'undefined');
\r
1291 angular.module('ui.tree')
\r
1295 * @name ui.tree.service:UiTreeHelper
\r
1296 * @requires ng.$document
\r
1297 * @requires ng.$window
\r
1300 * angular-ui-tree.
\r
1302 .factory('UiTreeHelper', ['$document', '$window', 'treeConfig',
\r
1303 function ($document, $window, treeConfig) {
\r
1307 * A hashtable used to storage data of nodes
\r
1312 setNodeAttribute: function (scope, attrName, val) {
\r
1313 if (!scope.$modelValue) {
\r
1316 var data = this.nodesData[scope.$modelValue.$$hashKey];
\r
1319 this.nodesData[scope.$modelValue.$$hashKey] = data;
\r
1321 data[attrName] = val;
\r
1324 getNodeAttribute: function (scope, attrName) {
\r
1325 if (!scope.$modelValue) {
\r
1328 var data = this.nodesData[scope.$modelValue.$$hashKey];
\r
1330 return data[attrName];
\r
1337 * @methodOf ui.tree.service:$nodrag
\r
1338 * @param {Object} targetElm angular element
\r
1339 * @return {Bool} check if the node can be dragged.
\r
1341 nodrag: function (targetElm) {
\r
1342 if (typeof targetElm.attr('data-nodrag') != 'undefined') {
\r
1343 return targetElm.attr('data-nodrag') !== 'false';
\r
1349 * get the event object for touches
\r
1350 * @param {[type]} e [description]
\r
1351 * @return {[type]} [description]
\r
1353 eventObj: function (e) {
\r
1355 if (e.targetTouches !== undefined) {
\r
1356 obj = e.targetTouches.item(0);
\r
1357 } else if (e.originalEvent !== undefined && e.originalEvent.targetTouches !== undefined) {
\r
1358 obj = e.originalEvent.targetTouches.item(0);
\r
1363 dragInfo: function (node) {
\r
1367 cloneModel: node.$treeScope.cloneEnabled === true ? angular.copy(node.$modelValue) : undefined,
\r
1369 index: node.index(),
\r
1370 nodesScope: node.$parentNodesScope
\r
1372 index: node.index(),
\r
1373 siblings: node.siblings().slice(0),
\r
1374 parent: node.$parentNodesScope,
\r
1376 // Move the node to a new position
\r
1377 moveTo: function (parent, siblings, index) {
\r
1378 this.parent = parent;
\r
1379 this.siblings = siblings.slice(0);
\r
1381 // If source node is in the target nodes
\r
1382 var i = this.siblings.indexOf(this.source);
\r
1384 this.siblings.splice(i, 1);
\r
1385 if (this.source.index() < index) {
\r
1390 this.siblings.splice(index, 0, this.source);
\r
1391 this.index = index;
\r
1394 parentNode: function () {
\r
1395 return this.parent.$nodeScope;
\r
1398 prev: function () {
\r
1399 if (this.index > 0) {
\r
1400 return this.siblings[this.index - 1];
\r
1406 next: function () {
\r
1407 if (this.index < this.siblings.length - 1) {
\r
1408 return this.siblings[this.index + 1];
\r
1414 isClone: function () {
\r
1415 return this.source.$treeScope.cloneEnabled === true;
\r
1418 clonedNode: function (node) {
\r
1419 return angular.copy(node);
\r
1422 isDirty: function () {
\r
1423 return this.source.$parentNodesScope != this.parent ||
\r
1424 this.source.index() != this.index;
\r
1427 isForeign: function () {
\r
1428 return this.source.$treeScope !== this.parent.$treeScope;
\r
1431 eventArgs: function (elements, pos) {
\r
1433 source: this.sourceInfo,
\r
1435 index: this.index,
\r
1436 nodesScope: this.parent
\r
1438 elements: elements,
\r
1443 apply: function () {
\r
1445 var nodeData = this.source.$modelValue;
\r
1447 // nodrop enabled on tree or parent
\r
1448 if (this.parent.nodropEnabled || this.parent.$treeScope.nodropEnabled) {
\r
1452 // node was dropped in the same place - do nothing
\r
1453 if (!this.isDirty()) {
\r
1457 // cloneEnabled and cross-tree so copy and do not remove from source
\r
1458 if (this.isClone() && this.isForeign()) {
\r
1459 this.parent.insertNode(this.index, this.sourceInfo.cloneModel);
\r
1460 } else { // Any other case, remove and reinsert
\r
1461 this.source.remove();
\r
1462 this.parent.insertNode(this.index, nodeData);
\r
1470 * @name ui.tree#height
\r
1471 * @methodOf ui.tree.service:UiTreeHelper
\r
1474 * Get the height of an element.
\r
1476 * @param {Object} element Angular element.
\r
1477 * @returns {String} Height
\r
1479 height: function (element) {
\r
1480 return element.prop('scrollHeight');
\r
1485 * @name ui.tree#width
\r
1486 * @methodOf ui.tree.service:UiTreeHelper
\r
1489 * Get the width of an element.
\r
1491 * @param {Object} element Angular element.
\r
1492 * @returns {String} Width
\r
1494 width: function (element) {
\r
1495 return element.prop('scrollWidth');
\r
1500 * @name ui.tree#offset
\r
1501 * @methodOf ui.nestedSortable.service:UiTreeHelper
\r
1504 * Get the offset values of an element.
\r
1506 * @param {Object} element Angular element.
\r
1507 * @returns {Object} Object with properties width, height, top and left
\r
1509 offset: function (element) {
\r
1510 var boundingClientRect = element[0].getBoundingClientRect();
\r
1513 width: element.prop('offsetWidth'),
\r
1514 height: element.prop('offsetHeight'),
\r
1515 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
\r
1516 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
\r
1522 * @name ui.tree#positionStarted
\r
1523 * @methodOf ui.tree.service:UiTreeHelper
\r
1526 * Get the start position of the target element according to the provided event properties.
\r
1528 * @param {Object} e Event
\r
1529 * @param {Object} target Target element
\r
1530 * @returns {Object} Object with properties offsetX, offsetY, startX, startY, nowX and dirX.
\r
1532 positionStarted: function (e, target) {
\r
1537 if (e.originalEvent && e.originalEvent.touches && (e.originalEvent.touches.length > 0)) {
\r
1538 pageX = e.originalEvent.touches[0].pageX;
\r
1539 pageY = e.originalEvent.touches[0].pageY;
\r
1541 pos.offsetX = pageX - this.offset(target).left;
\r
1542 pos.offsetY = pageY - this.offset(target).top;
\r
1543 pos.startX = pos.lastX = pageX;
\r
1544 pos.startY = pos.lastY = pageY;
\r
1545 pos.nowX = pos.nowY = pos.distX = pos.distY = pos.dirAx = 0;
\r
1546 pos.dirX = pos.dirY = pos.lastDirX = pos.lastDirY = pos.distAxX = pos.distAxY = 0;
\r
1550 positionMoved: function (e, pos, firstMoving) {
\r
1551 var pageX = e.pageX,
\r
1554 if (e.originalEvent && e.originalEvent.touches && (e.originalEvent.touches.length > 0)) {
\r
1555 pageX = e.originalEvent.touches[0].pageX;
\r
1556 pageY = e.originalEvent.touches[0].pageY;
\r
1558 // mouse position last events
\r
1559 pos.lastX = pos.nowX;
\r
1560 pos.lastY = pos.nowY;
\r
1562 // mouse position this events
\r
1566 // distance mouse moved between events
\r
1567 pos.distX = pos.nowX - pos.lastX;
\r
1568 pos.distY = pos.nowY - pos.lastY;
\r
1570 // direction mouse was moving
\r
1571 pos.lastDirX = pos.dirX;
\r
1572 pos.lastDirY = pos.dirY;
\r
1574 // direction mouse is now moving (on both axis)
\r
1575 pos.dirX = pos.distX === 0 ? 0 : pos.distX > 0 ? 1 : -1;
\r
1576 pos.dirY = pos.distY === 0 ? 0 : pos.distY > 0 ? 1 : -1;
\r
1578 // axis mouse is now moving on
\r
1579 newAx = Math.abs(pos.distX) > Math.abs(pos.distY) ? 1 : 0;
\r
1581 // do nothing on first move
\r
1582 if (firstMoving) {
\r
1583 pos.dirAx = newAx;
\r
1584 pos.moving = true;
\r
1588 // calc distance moved on this axis (and direction)
\r
1589 if (pos.dirAx !== newAx) {
\r
1593 pos.distAxX += Math.abs(pos.distX);
\r
1594 if (pos.dirX !== 0 && pos.dirX !== pos.lastDirX) {
\r
1598 pos.distAxY += Math.abs(pos.distY);
\r
1599 if (pos.dirY !== 0 && pos.dirY !== pos.lastDirY) {
\r
1604 pos.dirAx = newAx;
\r
1607 elementIsTreeNode: function (element) {
\r
1608 return typeof element.attr('ui-tree-node') !== 'undefined';
\r
1611 elementIsTreeNodeHandle: function (element) {
\r
1612 return typeof element.attr('ui-tree-handle') !== 'undefined';
\r
1614 elementIsTree: function (element) {
\r
1615 return typeof element.attr('ui-tree') !== 'undefined';
\r
1617 elementIsTreeNodes: function (element) {
\r
1618 return typeof element.attr('ui-tree-nodes') !== 'undefined';
\r
1620 elementIsPlaceholder: function (element) {
\r
1621 return element.hasClass(treeConfig.placeholderClass);
\r
1623 elementContainsTreeNodeHandler: function (element) {
\r
1624 return element[0].querySelectorAll('[ui-tree-handle]').length >= 1;
\r
1626 treeNodeHandlerContainerOfElement: function (element) {
\r
1627 return findFirstParentElementWithAttribute('ui-tree-handle', element[0]);
\r
1633 // TODO: optimize this loop
\r
1634 function findFirstParentElementWithAttribute(attributeName, childObj) {
\r
1635 // undefined if the mouse leaves the browser window
\r
1636 if (childObj === undefined) {
\r
1639 var testObj = childObj.parentNode,
\r
1641 // check for setAttribute due to exception thrown by Firefox when a node is dragged outside the browser window
\r
1642 res = (typeof testObj.setAttribute === 'function' && testObj.hasAttribute(attributeName)) ? testObj : null;
\r
1643 while (testObj && typeof testObj.setAttribute === 'function' && !testObj.hasAttribute(attributeName)) {
\r
1644 testObj = testObj.parentNode;
\r
1646 if (testObj === document.documentElement) {
\r