diff options
Diffstat (limited to 'vid-app-common/src/main/webapp/app/vid/scripts/angular-ui-tree.js')
-rwxr-xr-x | vid-app-common/src/main/webapp/app/vid/scripts/angular-ui-tree.js | 1656 |
1 files changed, 1656 insertions, 0 deletions
diff --git a/vid-app-common/src/main/webapp/app/vid/scripts/angular-ui-tree.js b/vid-app-common/src/main/webapp/app/vid/scripts/angular-ui-tree.js new file mode 100755 index 00000000..6ec90799 --- /dev/null +++ b/vid-app-common/src/main/webapp/app/vid/scripts/angular-ui-tree.js @@ -0,0 +1,1656 @@ +/*-
+ * ============LICENSE_START=======================================================
+ * VID
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ * @license Angular UI Tree v2.17.0
+ * (c) 2010-2016. https://github.com/angular-ui-tree/angular-ui-tree
+ * License: MIT
+ */
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree', [])
+ .constant('treeConfig', {
+ treeClass: 'angular-ui-tree',
+ emptyTreeClass: 'angular-ui-tree-empty',
+ hiddenClass: 'angular-ui-tree-hidden',
+ nodesClass: 'angular-ui-tree-nodes',
+ nodeClass: 'angular-ui-tree-node',
+ handleClass: 'angular-ui-tree-handle',
+ placeholderClass: 'angular-ui-tree-placeholder',
+ dragClass: 'angular-ui-tree-drag',
+ dragThreshold: 3,
+ levelThreshold: 30,
+ defaultCollapsed: false
+ });
+
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+
+ .controller('TreeHandleController', ['$scope', '$element',
+ function ($scope, $element) {
+ this.scope = $scope;
+
+ $scope.$element = $element;
+ $scope.$nodeScope = null;
+ $scope.$type = 'uiTreeHandle';
+
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+ .controller('TreeNodeController', ['$scope', '$element',
+ function ($scope, $element) {
+ this.scope = $scope;
+
+ $scope.$element = $element;
+ $scope.$modelValue = null; // Model value for node;
+ $scope.$parentNodeScope = null; // uiTreeNode Scope of parent node;
+ $scope.$childNodesScope = null; // uiTreeNodes Scope of child nodes.
+ $scope.$parentNodesScope = null; // uiTreeNodes Scope of parent nodes.
+ $scope.$treeScope = null; // uiTree scope
+ $scope.$handleScope = null; // it's handle scope
+ $scope.$type = 'uiTreeNode';
+ $scope.$$allowNodeDrop = false;
+ $scope.collapsed = false;
+ $scope.expandOnHover = false;
+
+ $scope.init = function (controllersArr) {
+ var treeNodesCtrl = controllersArr[0];
+ $scope.$treeScope = controllersArr[1] ? controllersArr[1].scope : null;
+
+ // find the scope of it's parent node
+ $scope.$parentNodeScope = treeNodesCtrl.scope.$nodeScope;
+ // modelValue for current node
+ $scope.$modelValue = treeNodesCtrl.scope.$modelValue[$scope.$index];
+ $scope.$parentNodesScope = treeNodesCtrl.scope;
+ treeNodesCtrl.scope.initSubNode($scope); // init sub nodes
+
+ $element.on('$destroy', function () {
+ treeNodesCtrl.scope.destroySubNode($scope); // destroy sub nodes
+ });
+ };
+
+ $scope.index = function () {
+ return $scope.$parentNodesScope.$modelValue.indexOf($scope.$modelValue);
+ };
+
+ $scope.dragEnabled = function () {
+ return !($scope.$treeScope && !$scope.$treeScope.dragEnabled);
+ };
+
+ $scope.isSibling = function (targetNode) {
+ return $scope.$parentNodesScope == targetNode.$parentNodesScope;
+ };
+
+ $scope.isChild = function (targetNode) {
+ var nodes = $scope.childNodes();
+ return nodes && nodes.indexOf(targetNode) > -1;
+ };
+
+ $scope.prev = function () {
+ var index = $scope.index();
+ if (index > 0) {
+ return $scope.siblings()[index - 1];
+ }
+ return null;
+ };
+
+ $scope.siblings = function () {
+ return $scope.$parentNodesScope.childNodes();
+ };
+
+ $scope.childNodesCount = function () {
+ return $scope.childNodes() ? $scope.childNodes().length : 0;
+ };
+
+ $scope.hasChild = function () {
+ return $scope.childNodesCount() > 0;
+ };
+
+ $scope.childNodes = function () {
+ return $scope.$childNodesScope && $scope.$childNodesScope.$modelValue ?
+ $scope.$childNodesScope.childNodes() :
+ null;
+ };
+
+ $scope.accept = function (sourceNode, destIndex) {
+ return $scope.$childNodesScope &&
+ $scope.$childNodesScope.$modelValue &&
+ $scope.$childNodesScope.accept(sourceNode, destIndex);
+ };
+
+ $scope.remove = function () {
+ return $scope.$parentNodesScope.removeNode($scope);
+ };
+
+ $scope.toggle = function () {
+ $scope.collapsed = !$scope.collapsed;
+ $scope.$treeScope.$callbacks.toggle($scope.collapsed, $scope);
+ };
+
+ $scope.collapse = function () {
+ $scope.collapsed = true;
+ };
+
+ $scope.expand = function () {
+ $scope.collapsed = false;
+ };
+
+ $scope.depth = function () {
+ var parentNode = $scope.$parentNodeScope;
+ if (parentNode) {
+ return parentNode.depth() + 1;
+ }
+ return 1;
+ };
+
+ /**
+ * Returns the depth of the deepest subtree under this node
+ * @param scope a TreeNodesController scope object
+ * @returns Depth of all nodes *beneath* this node. If scope belongs to a leaf node, the
+ * result is 0 (it has no subtree).
+ */
+ function countSubTreeDepth(scope) {
+ var thisLevelDepth = 0,
+ childNodes = scope.childNodes(),
+ childNode,
+ childDepth,
+ i;
+ if (!childNodes || childNodes.length === 0) {
+ return 0;
+ }
+ for (i = childNodes.length - 1; i >= 0 ; i--) {
+ childNode = childNodes[i],
+ childDepth = 1 + countSubTreeDepth(childNode);
+ thisLevelDepth = Math.max(thisLevelDepth, childDepth);
+ }
+ return thisLevelDepth;
+ }
+
+ $scope.maxSubDepth = function () {
+ return $scope.$childNodesScope ? countSubTreeDepth($scope.$childNodesScope) : 0;
+ };
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+
+ .controller('TreeNodesController', ['$scope', '$element',
+ function ($scope, $element) {
+ this.scope = $scope;
+
+ $scope.$element = $element;
+ $scope.$modelValue = null;
+ $scope.$nodeScope = null; // the scope of node which the nodes belongs to
+ $scope.$treeScope = null;
+ $scope.$type = 'uiTreeNodes';
+ $scope.$nodesMap = {};
+
+ $scope.nodropEnabled = false;
+ $scope.maxDepth = 0;
+ $scope.cloneEnabled = false;
+
+ $scope.initSubNode = function (subNode) {
+ if (!subNode.$modelValue) {
+ return null;
+ }
+ $scope.$nodesMap[subNode.$modelValue.$$hashKey] = subNode;
+ };
+
+ $scope.destroySubNode = function (subNode) {
+ if (!subNode.$modelValue) {
+ return null;
+ }
+ $scope.$nodesMap[subNode.$modelValue.$$hashKey] = null;
+ };
+
+ $scope.accept = function (sourceNode, destIndex) {
+ return $scope.$treeScope.$callbacks.accept(sourceNode, $scope, destIndex);
+ };
+
+ $scope.beforeDrag = function (sourceNode) {
+ return $scope.$treeScope.$callbacks.beforeDrag(sourceNode);
+ };
+
+ $scope.isParent = function (node) {
+ return node.$parentNodesScope == $scope;
+ };
+
+ $scope.hasChild = function () {
+ return $scope.$modelValue.length > 0;
+ };
+
+ $scope.safeApply = function (fn) {
+ var phase = this.$root.$$phase;
+ if (phase == '$apply' || phase == '$digest') {
+ if (fn && (typeof (fn) === 'function')) {
+ fn();
+ }
+ } else {
+ this.$apply(fn);
+ }
+ };
+
+ $scope.removeNode = function (node) {
+ var index = $scope.$modelValue.indexOf(node.$modelValue);
+ if (index > -1) {
+ $scope.safeApply(function () {
+ $scope.$modelValue.splice(index, 1)[0];
+ });
+ return $scope.$treeScope.$callbacks.removed(node);
+ }
+ return null;
+ };
+
+ $scope.insertNode = function (index, nodeData) {
+ $scope.safeApply(function () {
+ $scope.$modelValue.splice(index, 0, nodeData);
+ });
+ };
+
+ $scope.childNodes = function () {
+ var i, nodes = [];
+ if ($scope.$modelValue) {
+ for (i = 0; i < $scope.$modelValue.length; i++) {
+ nodes.push($scope.$nodesMap[$scope.$modelValue[i].$$hashKey]);
+ }
+ }
+ return nodes;
+ };
+
+ $scope.depth = function () {
+ if ($scope.$nodeScope) {
+ return $scope.$nodeScope.depth();
+ }
+ return 0; // if it has no $nodeScope, it's root
+ };
+
+ // check if depth limit has reached
+ $scope.outOfDepth = function (sourceNode) {
+ var maxDepth = $scope.maxDepth || $scope.$treeScope.maxDepth;
+ if (maxDepth > 0) {
+ return $scope.depth() + sourceNode.maxSubDepth() + 1 > maxDepth;
+ }
+ return false;
+ };
+
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+
+ .controller('TreeController', ['$scope', '$element',
+ function ($scope, $element) {
+ this.scope = $scope;
+
+ $scope.$element = $element;
+ $scope.$nodesScope = null; // root nodes
+ $scope.$type = 'uiTree';
+ $scope.$emptyElm = null;
+ $scope.$callbacks = null;
+
+ $scope.dragEnabled = true;
+ $scope.emptyPlaceholderEnabled = true;
+ $scope.maxDepth = 0;
+ $scope.dragDelay = 0;
+ $scope.cloneEnabled = false;
+ $scope.nodropEnabled = false;
+
+ // Check if it's a empty tree
+ $scope.isEmpty = function () {
+ return ($scope.$nodesScope && $scope.$nodesScope.$modelValue
+ && $scope.$nodesScope.$modelValue.length === 0);
+ };
+
+ // add placeholder to empty tree
+ $scope.place = function (placeElm) {
+ $scope.$nodesScope.$element.append(placeElm);
+ $scope.$emptyElm.remove();
+ };
+
+ this.resetEmptyElement = function () {
+ if ((!$scope.$nodesScope.$modelValue || $scope.$nodesScope.$modelValue.length === 0) &&
+ $scope.emptyPlaceholderEnabled) {
+ $element.append($scope.$emptyElm);
+ } else {
+ $scope.$emptyElm.remove();
+ }
+ };
+
+ $scope.resetEmptyElement = this.resetEmptyElement;
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+ .directive('uiTree', ['treeConfig', '$window',
+ function (treeConfig, $window) {
+ return {
+ restrict: 'A',
+ scope: true,
+ controller: 'TreeController',
+ link: function (scope, element, attrs, ctrl) {
+ var callbacks = {
+ accept: null,
+ beforeDrag: null
+ },
+ config = {},
+ tdElm,
+ $trElm,
+ emptyElmColspan;
+
+ angular.extend(config, treeConfig);
+ if (config.treeClass) {
+ element.addClass(config.treeClass);
+ }
+
+ if (element.prop('tagName').toLowerCase() === 'table') {
+ scope.$emptyElm = angular.element($window.document.createElement('tr'));
+ $trElm = element.find('tr');
+ // If we can find a tr, then we can use its td children as the empty element colspan.
+ if ($trElm.length > 0) {
+ emptyElmColspan = angular.element($trElm).children().length;
+ } else {
+ // If not, by setting a huge colspan we make sure it takes full width.
+ emptyElmColspan = 1000000;
+ }
+ tdElm = angular.element($window.document.createElement('td'))
+ .attr('colspan', emptyElmColspan);
+ scope.$emptyElm.append(tdElm);
+ } else {
+ scope.$emptyElm = angular.element($window.document.createElement('div'));
+ }
+
+ if (config.emptyTreeClass) {
+ scope.$emptyElm.addClass(config.emptyTreeClass);
+ }
+
+ scope.$watch('$nodesScope.$modelValue.length', function (val) {
+ if (!angular.isNumber(val)) {
+ return;
+ }
+
+ ctrl.resetEmptyElement();
+ }, true);
+
+ scope.$watch(attrs.dragEnabled, function (val) {
+ if ((typeof val) == 'boolean') {
+ scope.dragEnabled = val;
+ }
+ });
+
+ scope.$watch(attrs.emptyPlaceholderEnabled, function (val) {
+ if ((typeof val) == 'boolean') {
+ scope.emptyPlaceholderEnabled = val;
+ ctrl.resetEmptyElement();
+ }
+ });
+
+ scope.$watch(attrs.nodropEnabled, function (val) {
+ if ((typeof val) == 'boolean') {
+ scope.nodropEnabled = val;
+ }
+ });
+
+ scope.$watch(attrs.cloneEnabled, function (val) {
+ if ((typeof val) == 'boolean') {
+ scope.cloneEnabled = val;
+ }
+ });
+
+ scope.$watch(attrs.maxDepth, function (val) {
+ if ((typeof val) == 'number') {
+ scope.maxDepth = val;
+ }
+ });
+
+ scope.$watch(attrs.dragDelay, function (val) {
+ if ((typeof val) == 'number') {
+ scope.dragDelay = val;
+ }
+ });
+
+ /**
+ * Callback checks if the destination node can accept the dragged node.
+ * By default, ui-tree will check that 'data-nodrop-enabled' is not set for the
+ * destination ui-tree-nodes, and that the 'max-depth' attribute will not be exceeded
+ * if it is set on the ui-tree or ui-tree-nodes.
+ * This callback can be overridden, but callers must manually enforce nodrop and max-depth
+ * themselves if they need those to be enforced.
+ * @param sourceNodeScope Scope of the ui-tree-node being dragged
+ * @param destNodesScope Scope of the ui-tree-nodes where the node is hovering
+ * @param destIndex Index in the destination nodes array where the source node will drop
+ * @returns {boolean} True if the node is permitted to be dropped here
+ */
+ callbacks.accept = function (sourceNodeScope, destNodesScope, destIndex) {
+ return !(destNodesScope.nodropEnabled || destNodesScope.$treeScope.nodropEnabled || destNodesScope.outOfDepth(sourceNodeScope));
+ };
+
+ callbacks.beforeDrag = function (sourceNodeScope) {
+ return true;
+ };
+
+ callbacks.expandTimeoutStart = function()
+ {
+
+ };
+
+ callbacks.expandTimeoutCancel = function()
+ {
+
+ };
+
+ callbacks.expandTimeoutEnd = function()
+ {
+
+ };
+
+ callbacks.removed = function (node) {
+
+ };
+
+ /**
+ * Callback is fired when a node is successfully dropped in a new location
+ * @param event
+ */
+ callbacks.dropped = function (event) {
+
+ };
+
+ /**
+ * Callback is fired each time the user starts dragging a node
+ * @param event
+ */
+ callbacks.dragStart = function (event) {
+
+ };
+
+ /**
+ * Callback is fired each time a dragged node is moved with the mouse/touch.
+ * @param event
+ */
+ callbacks.dragMove = function (event) {
+
+ };
+
+ /**
+ * Callback is fired when the tree exits drag mode. If the user dropped a node, the drop may have been
+ * accepted or reverted.
+ * @param event
+ */
+ callbacks.dragStop = function (event) {
+
+ };
+
+ /**
+ * Callback is fired when a user drops a node (but prior to processing the drop action)
+ * beforeDrop can return a Promise, truthy, or falsy (returning nothing is falsy).
+ * If it returns falsy, or a resolve Promise, the node move is accepted
+ * If it returns truthy, or a rejected Promise, the node move is reverted
+ * @param event
+ * @returns {Boolean|Promise} Truthy (or rejected Promise) to cancel node move; falsy (or resolved promise)
+ */
+ callbacks.beforeDrop = function (event) {
+
+ };
+
+ /**
+ * Callback is fired when a user toggles node (but after processing the toggle action)
+ * @param sourceNodeScope
+ * @param collapsed
+ */
+ callbacks.toggle = function (collapsed, sourceNodeScope) {
+
+ };
+
+ scope.$watch(attrs.uiTree, function (newVal, oldVal) {
+ angular.forEach(newVal, function (value, key) {
+ if (callbacks[key]) {
+ if (typeof value === 'function') {
+ callbacks[key] = value;
+ }
+ }
+ });
+
+ scope.$callbacks = callbacks;
+ }, true);
+
+
+ }
+ };
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+ .directive('uiTreeHandle', ['treeConfig',
+ function (treeConfig) {
+ return {
+ require: '^uiTreeNode',
+ restrict: 'A',
+ scope: true,
+ controller: 'TreeHandleController',
+ link: function (scope, element, attrs, treeNodeCtrl) {
+ var config = {};
+ angular.extend(config, treeConfig);
+ if (config.handleClass) {
+ element.addClass(config.handleClass);
+ }
+ // connect with the tree node.
+ if (scope != treeNodeCtrl.scope) {
+ scope.$nodeScope = treeNodeCtrl.scope;
+ treeNodeCtrl.scope.$handleScope = scope;
+ }
+ }
+ };
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+
+ .directive('uiTreeNode', ['treeConfig', 'UiTreeHelper', '$window', '$document', '$timeout', '$q',
+ function (treeConfig, UiTreeHelper, $window, $document, $timeout, $q) {
+ return {
+ require: ['^uiTreeNodes', '^uiTree'],
+ restrict: 'A',
+ controller: 'TreeNodeController',
+ link: function (scope, element, attrs, controllersArr) {
+ // todo startPos is unused
+ var config = {},
+ hasTouch = 'ontouchstart' in window,
+ startPos, firstMoving, dragInfo, pos,
+ placeElm, hiddenPlaceElm, dragElm,
+ treeScope = null,
+ elements, // As a parameter for callbacks
+ dragDelaying = true,
+ dragStarted = false,
+ dragTimer = null,
+ body = document.body,
+ html = document.documentElement,
+ document_height,
+ document_width,
+ dragStart,
+ tagName,
+ dragMove,
+ dragEnd,
+ dragStartEvent,
+ dragMoveEvent,
+ dragEndEvent,
+ dragCancelEvent,
+ dragDelay,
+ bindDragStartEvents,
+ bindDragMoveEvents,
+ unbindDragMoveEvents,
+ keydownHandler,
+ outOfBounds,
+ isHandleChild,
+ el;
+
+ angular.extend(config, treeConfig);
+ if (config.nodeClass) {
+ element.addClass(config.nodeClass);
+ }
+ scope.init(controllersArr);
+
+ scope.collapsed = !!UiTreeHelper.getNodeAttribute(scope, 'collapsed') || treeConfig.defaultCollapsed;
+ scope.expandOnHover = !!UiTreeHelper.getNodeAttribute(scope, 'expandOnHover');
+ scope.sourceOnly = scope.nodropEnabled || scope.$treeScope.nodropEnabled;
+
+ scope.$watch(attrs.collapsed, function (val) {
+ if ((typeof val) == 'boolean') {
+ scope.collapsed = val;
+ }
+ });
+
+ scope.$watch('collapsed', function (val) {
+ UiTreeHelper.setNodeAttribute(scope, 'collapsed', val);
+ attrs.$set('collapsed', val);
+ });
+
+ scope.$watch(attrs.expandOnHover, function(val) {
+ if ((typeof val) == 'boolean') {
+ scope.expandOnHover = val;
+ }
+ });
+
+ scope.$watch('expandOnHover', function (val) {
+ UiTreeHelper.setNodeAttribute(scope, 'expandOnHover', val);
+ attrs.$set('expandOnHover', val);
+ });
+
+ scope.$on('angular-ui-tree:collapse-all', function () {
+ scope.collapsed = true;
+ });
+
+ scope.$on('angular-ui-tree:expand-all', function () {
+ scope.collapsed = false;
+ });
+
+ /**
+ * Called when the user has grabbed a node and started dragging it
+ * @param e
+ */
+ dragStart = function (e) {
+ // disable right click
+ if (!hasTouch && (e.button === 2 || e.which === 3)) {
+ return;
+ }
+
+ // event has already fired in other scope
+ if (e.uiTreeDragging || (e.originalEvent && e.originalEvent.uiTreeDragging)) {
+ return;
+ }
+
+ // the node being dragged
+ var eventElm = angular.element(e.target),
+ isHandleChild, cloneElm, eventElmTagName, tagName,
+ eventObj, tdElm, hStyle,
+ isTreeNode,
+ isTreeNodeHandle;
+
+ // if the target element is a child element of a ui-tree-handle,
+ // use the containing handle element as target element
+ isHandleChild = UiTreeHelper.treeNodeHandlerContainerOfElement(eventElm);
+ if (isHandleChild) {
+ eventElm = angular.element(isHandleChild);
+ }
+
+ cloneElm = element.clone();
+ isTreeNode = UiTreeHelper.elementIsTreeNode(eventElm);
+ isTreeNodeHandle = UiTreeHelper.elementIsTreeNodeHandle(eventElm);
+
+ if (!isTreeNode && !isTreeNodeHandle) {
+ return;
+ }
+
+ if (isTreeNode && UiTreeHelper.elementContainsTreeNodeHandler(eventElm)) {
+ return;
+ }
+
+ eventElmTagName = eventElm.prop('tagName').toLowerCase();
+ if (eventElmTagName == 'input' ||
+ eventElmTagName == 'textarea' ||
+ eventElmTagName == 'button' ||
+ eventElmTagName == 'select') { // if it's a input or button, ignore it
+ return;
+ }
+
+ // check if it or it's parents has a 'data-nodrag' attribute
+ el = angular.element(e.target);
+ while (el && el[0] && el[0] !== element) {
+ if (UiTreeHelper.nodrag(el)) { // if the node mark as `nodrag`, DONOT drag it.
+ return;
+ }
+ el = el.parent();
+ }
+
+ if (!scope.beforeDrag(scope)) {
+ return;
+ }
+
+ e.uiTreeDragging = true; // stop event bubbling
+ if (e.originalEvent) {
+ e.originalEvent.uiTreeDragging = true;
+ }
+ e.preventDefault();
+ eventObj = UiTreeHelper.eventObj(e);
+
+ firstMoving = true;
+ dragInfo = UiTreeHelper.dragInfo(scope);
+
+ tagName = element.prop('tagName');
+
+ if (tagName.toLowerCase() === 'tr') {
+ placeElm = angular.element($window.document.createElement(tagName));
+ tdElm = angular.element($window.document.createElement('td'))
+ .addClass(config.placeholderClass)
+ .attr('colspan', element[0].children.length);
+ placeElm.append(tdElm);
+ } else {
+ placeElm = angular.element($window.document.createElement(tagName))
+ .addClass(config.placeholderClass);
+ }
+ hiddenPlaceElm = angular.element($window.document.createElement(tagName));
+ if (config.hiddenClass) {
+ hiddenPlaceElm.addClass(config.hiddenClass);
+ }
+
+ pos = UiTreeHelper.positionStarted(eventObj, element);
+ placeElm.css('height', UiTreeHelper.height(element) + 'px');
+
+ dragElm = angular.element($window.document.createElement(scope.$parentNodesScope.$element.prop('tagName')))
+ .addClass(scope.$parentNodesScope.$element.attr('class')).addClass(config.dragClass);
+ dragElm.css('width', UiTreeHelper.width(element) + 'px');
+ dragElm.css('z-index', 9999);
+
+ // Prevents cursor to change rapidly in Opera 12.16 and IE when dragging an element
+ hStyle = (element[0].querySelector('.angular-ui-tree-handle') || element[0]).currentStyle;
+ if (hStyle) {
+ document.body.setAttribute('ui-tree-cursor', $document.find('body').css('cursor') || '');
+ $document.find('body').css({'cursor': hStyle.cursor + '!important'});
+ }
+
+ if (scope.sourceOnly) {
+ placeElm.css('display', 'none');
+ }
+ element.after(placeElm);
+ element.after(hiddenPlaceElm);
+ if (dragInfo.isClone() && scope.sourceOnly) {
+ dragElm.append(cloneElm);
+ } else {
+ dragElm.append(element);
+ }
+
+ $document.find('body').append(dragElm);
+
+ dragElm.css({
+ 'left': eventObj.pageX - pos.offsetX + 'px',
+ 'top': eventObj.pageY - pos.offsetY + 'px'
+ });
+ elements = {
+ placeholder: placeElm,
+ dragging: dragElm
+ };
+
+ bindDragMoveEvents();
+ // Fire dragStart callback
+ scope.$apply(function () {
+ scope.$treeScope.$callbacks.dragStart(dragInfo.eventArgs(elements, pos));
+ });
+
+ document_height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
+ document_width = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
+ };
+
+ dragMove = function (e) {
+ var eventObj = UiTreeHelper.eventObj(e),
+ prev,
+ next,
+ leftElmPos,
+ topElmPos,
+ top_scroll,
+ bottom_scroll,
+ target,
+ decrease,
+ targetX,
+ targetY,
+ displayElm,
+ targetNode,
+ targetElm,
+ isEmpty,
+ scrollDownBy,
+ targetOffset,
+ targetBefore;
+
+ if (dragElm) {
+ e.preventDefault();
+
+ if ($window.getSelection) {
+ $window.getSelection().removeAllRanges();
+ } else if ($window.document.selection) {
+ $window.document.selection.empty();
+ }
+
+ leftElmPos = eventObj.pageX - pos.offsetX;
+ topElmPos = eventObj.pageY - pos.offsetY;
+
+ //dragElm can't leave the screen on the left
+ if (leftElmPos < 0) {
+ leftElmPos = 0;
+ }
+
+ //dragElm can't leave the screen on the top
+ if (topElmPos < 0) {
+ topElmPos = 0;
+ }
+
+ //dragElm can't leave the screen on the bottom
+ if ((topElmPos + 10) > document_height) {
+ topElmPos = document_height - 10;
+ }
+
+ //dragElm can't leave the screen on the right
+ if ((leftElmPos + 10) > document_width) {
+ leftElmPos = document_width - 10;
+ }
+
+ dragElm.css({
+ 'left': leftElmPos + 'px',
+ 'top': topElmPos + 'px'
+ });
+
+ top_scroll = window.pageYOffset || $window.document.documentElement.scrollTop;
+ bottom_scroll = top_scroll + (window.innerHeight || $window.document.clientHeight || $window.document.clientHeight);
+
+ // to scroll down if cursor y-position is greater than the bottom position the vertical scroll
+ if (bottom_scroll < eventObj.pageY && bottom_scroll < document_height) {
+ scrollDownBy = Math.min(document_height - bottom_scroll, 10);
+ window.scrollBy(0, scrollDownBy);
+ }
+
+ // to scroll top if cursor y-position is less than the top position the vertical scroll
+ if (top_scroll > eventObj.pageY) {
+ window.scrollBy(0, -10);
+ }
+
+ UiTreeHelper.positionMoved(e, pos, firstMoving);
+ if (firstMoving) {
+ firstMoving = false;
+ return;
+ }
+
+ // check if add it as a child node first
+ // todo decrease is unused
+ decrease = (UiTreeHelper.offset(dragElm).left - UiTreeHelper.offset(placeElm).left) >= config.threshold;
+
+ targetX = eventObj.pageX - ($window.pageXOffset ||
+ $window.document.body.scrollLeft ||
+ $window.document.documentElement.scrollLeft) -
+ ($window.document.documentElement.clientLeft || 0);
+
+ targetY = eventObj.pageY - ($window.pageYOffset ||
+ $window.document.body.scrollTop ||
+ $window.document.documentElement.scrollTop) -
+ ($window.document.documentElement.clientTop || 0);
+
+ // Select the drag target. Because IE does not support CSS 'pointer-events: none', it will always
+ // pick the drag element itself as the target. To prevent this, we hide the drag element while
+ // selecting the target.
+ if (angular.isFunction(dragElm.hide)) {
+ dragElm.hide();
+ } else {
+ displayElm = dragElm[0].style.display;
+ dragElm[0].style.display = 'none';
+ }
+
+ // when using elementFromPoint() inside an iframe, you have to call
+ // elementFromPoint() twice to make sure IE8 returns the correct value
+ $window.document.elementFromPoint(targetX, targetY);
+
+ targetElm = angular.element($window.document.elementFromPoint(targetX, targetY));
+
+ // if the target element is a child element of a ui-tree-handle,
+ // use the containing handle element as target element
+ isHandleChild = UiTreeHelper.treeNodeHandlerContainerOfElement(targetElm);
+ if (isHandleChild) {
+ targetElm = angular.element(isHandleChild);
+ }
+
+ if (angular.isFunction(dragElm.show)) {
+ dragElm.show();
+ } else {
+ dragElm[0].style.display = displayElm;
+ }
+
+ outOfBounds = !UiTreeHelper.elementIsTreeNodeHandle(targetElm) &&
+ !UiTreeHelper.elementIsTreeNode(targetElm) &&
+ !UiTreeHelper.elementIsTreeNodes(targetElm) &&
+ !UiTreeHelper.elementIsTree(targetElm) &&
+ !UiTreeHelper.elementIsPlaceholder(targetElm);
+
+ // Detect out of bounds condition, update drop target display, and prevent drop
+ if (outOfBounds) {
+
+ // Remove the placeholder
+ placeElm.remove();
+
+ // If the target was an empty tree, replace the empty element placeholder
+ if (treeScope) {
+ treeScope.resetEmptyElement();
+ treeScope = null;
+ }
+ }
+
+ // move horizontal
+ if (pos.dirAx && pos.distAxX >= config.levelThreshold) {
+ pos.distAxX = 0;
+
+ // increase horizontal level if previous sibling exists and is not collapsed
+ if (pos.distX > 0) {
+ prev = dragInfo.prev();
+ if (prev && !prev.collapsed
+ && prev.accept(scope, prev.childNodesCount())) {
+ prev.$childNodesScope.$element.append(placeElm);
+ dragInfo.moveTo(prev.$childNodesScope, prev.childNodes(), prev.childNodesCount());
+ }
+ }
+
+ // decrease horizontal level
+ if (pos.distX < 0) {
+ // we can't decrease a level if an item preceeds the current one
+ next = dragInfo.next();
+ if (!next) {
+ target = dragInfo.parentNode(); // As a sibling of it's parent node
+ if (target
+ && target.$parentNodesScope.accept(scope, target.index() + 1)) {
+ target.$element.after(placeElm);
+ dragInfo.moveTo(target.$parentNodesScope, target.siblings(), target.index() + 1);
+ }
+ }
+ }
+ }
+
+ // move vertical
+ if (!pos.dirAx) {
+ if (UiTreeHelper.elementIsTree(targetElm)) {
+ targetNode = targetElm.controller('uiTree').scope;
+ } else if (UiTreeHelper.elementIsTreeNodeHandle(targetElm)) {
+ targetNode = targetElm.controller('uiTreeHandle').scope;
+ } else if (UiTreeHelper.elementIsTreeNode(targetElm)) {
+ targetNode = targetElm.controller('uiTreeNode').scope;
+ } else if (UiTreeHelper.elementIsTreeNodes(targetElm)) {
+ targetNode = targetElm.controller('uiTreeNodes').scope;
+ } else if (UiTreeHelper.elementIsPlaceholder(targetElm)) {
+ targetNode = targetElm.controller('uiTreeNodes').scope;
+ } else if (targetElm.controller('uiTreeNode')) {
+ // is a child element of a node
+ targetNode = targetElm.controller('uiTreeNode').scope;
+ }
+
+ // check it's new position
+ isEmpty = false;
+ if (!targetNode) {
+ return;
+ }
+
+ // Show the placeholder if it was hidden for nodrop-enabled and this is a new tree
+ if (targetNode.$treeScope && !targetNode.$parent.nodropEnabled && !targetNode.$treeScope.nodropEnabled) {
+ placeElm.css('display', '');
+ }
+
+ if (targetNode.$type == 'uiTree' && targetNode.dragEnabled) {
+ isEmpty = targetNode.isEmpty(); // Check if it's empty tree
+ }
+
+ if (targetNode.$type == 'uiTreeHandle') {
+ targetNode = targetNode.$nodeScope;
+ }
+
+ if (targetNode.$type != 'uiTreeNode'
+ && !isEmpty) { // Check if it is a uiTreeNode or it's an empty tree
+ return;
+ }
+
+ // if placeholder move from empty tree, reset it.
+ if (treeScope && placeElm.parent()[0] != treeScope.$element[0]) {
+ treeScope.resetEmptyElement();
+ treeScope = null;
+ }
+
+ if (isEmpty) { // it's an empty tree
+ treeScope = targetNode;
+ if (targetNode.$nodesScope.accept(scope, 0)) {
+ targetNode.place(placeElm);
+ dragInfo.moveTo(targetNode.$nodesScope, targetNode.$nodesScope.childNodes(), 0);
+ }
+ } else if (targetNode.dragEnabled()) { // drag enabled
+ if (angular.isDefined(scope.expandTimeoutOn) && scope.expandTimeoutOn !== targetNode.id) {
+ $timeout.cancel(scope.expandTimeout);
+ delete scope.expandTimeout;
+ delete scope.expandTimeoutOn;
+
+ scope.$callbacks.expandTimeoutCancel();
+ }
+
+ if (targetNode.collapsed) {
+ if (scope.expandOnHover === true || (angular.isNumber(scope.expandOnHover) && scope.expandOnHover === 0)) {
+ targetNode.collapsed = false;
+ } else if (scope.expandOnHover !== false && angular.isNumber(scope.expandOnHover) && scope.expandOnHover > 0) {
+ if (angular.isUndefined(scope.expandTimeoutOn)) {
+ scope.expandTimeoutOn = targetNode.$id;
+
+ scope.$callbacks.expandTimeoutStart();
+ scope.expandTimeout = $timeout(function()
+ {
+ scope.$callbacks.expandTimeoutEnd();
+ targetNode.collapsed = false;
+ }, scope.expandOnHover);
+ }
+ }
+ }
+
+ targetElm = targetNode.$element; // Get the element of ui-tree-node
+ targetOffset = UiTreeHelper.offset(targetElm);
+ targetBefore = targetNode.horizontal ? eventObj.pageX < (targetOffset.left + UiTreeHelper.width(targetElm) / 2)
+ : eventObj.pageY < (targetOffset.top + UiTreeHelper.height(targetElm) / 2);
+
+ if (targetNode.$parentNodesScope.accept(scope, targetNode.index())) {
+ if (targetBefore) {
+ targetElm[0].parentNode.insertBefore(placeElm[0], targetElm[0]);
+ dragInfo.moveTo(targetNode.$parentNodesScope, targetNode.siblings(), targetNode.index());
+ } else {
+ targetElm.after(placeElm);
+ dragInfo.moveTo(targetNode.$parentNodesScope, targetNode.siblings(), targetNode.index() + 1);
+ }
+ } else if (!targetBefore && targetNode.accept(scope, targetNode.childNodesCount())) { // we have to check if it can add the dragging node as a child
+ targetNode.$childNodesScope.$element.append(placeElm);
+ dragInfo.moveTo(targetNode.$childNodesScope, targetNode.childNodes(), targetNode.childNodesCount());
+ } else {
+ outOfBounds = true;
+ }
+ }
+ }
+
+ scope.$apply(function () {
+ scope.$treeScope.$callbacks.dragMove(dragInfo.eventArgs(elements, pos));
+ });
+ }
+ };
+
+ dragEnd = function (e) {
+ var dragEventArgs = dragInfo.eventArgs(elements, pos);
+ e.preventDefault();
+ unbindDragMoveEvents();
+
+ $timeout.cancel(scope.expandTimeout);
+
+ scope.$treeScope.$apply(function () {
+ $q.when(scope.$treeScope.$callbacks.beforeDrop(dragEventArgs))
+ // promise resolved (or callback didn't return false)
+ .then(function (allowDrop) {
+ if (allowDrop !== false && scope.$$allowNodeDrop && !outOfBounds) { // node drop accepted)
+ dragInfo.apply();
+ // fire the dropped callback only if the move was successful
+ scope.$treeScope.$callbacks.dropped(dragEventArgs);
+ } else { // drop canceled - revert the node to its original position
+ bindDragStartEvents();
+ }
+ })
+ // promise rejected - revert the node to its original position
+ .catch(function () {
+ bindDragStartEvents();
+ })
+ .finally(function () {
+ hiddenPlaceElm.replaceWith(scope.$element);
+ placeElm.remove();
+
+ if (dragElm) { // drag element is attached to the mouse pointer
+ dragElm.remove();
+ dragElm = null;
+ }
+ scope.$treeScope.$callbacks.dragStop(dragEventArgs);
+ scope.$$allowNodeDrop = false;
+ dragInfo = null;
+
+ // Restore cursor in Opera 12.16 and IE
+ var oldCur = document.body.getAttribute('ui-tree-cursor');
+ if (oldCur !== null) {
+ $document.find('body').css({'cursor': oldCur});
+ document.body.removeAttribute('ui-tree-cursor');
+ }
+ });
+ });
+ };
+
+ dragStartEvent = function (e) {
+ if (scope.dragEnabled()) {
+ dragStart(e);
+ }
+ };
+
+ dragMoveEvent = function (e) {
+ dragMove(e);
+ };
+
+ dragEndEvent = function (e) {
+ scope.$$allowNodeDrop = true;
+ dragEnd(e);
+ };
+
+ dragCancelEvent = function (e) {
+ dragEnd(e);
+ };
+
+ dragDelay = (function () {
+ var to;
+
+ return {
+ exec: function (fn, ms) {
+ if (!ms) {
+ ms = 0;
+ }
+ this.cancel();
+ to = $timeout(fn, ms);
+ },
+ cancel: function () {
+ $timeout.cancel(to);
+ }
+ };
+ })();
+
+ /**
+ * Binds the mouse/touch events to enable drag start for this node
+ */
+ bindDragStartEvents = function () {
+ element.bind('touchstart mousedown', function (e) {
+ dragDelay.exec(function () {
+ dragStartEvent(e);
+ }, scope.dragDelay || 0);
+ });
+ element.bind('touchend touchcancel mouseup', function () {
+ dragDelay.cancel();
+ });
+ };
+ bindDragStartEvents();
+
+ /**
+ * Binds mouse/touch events that handle moving/dropping this dragged node
+ */
+ bindDragMoveEvents = function () {
+ angular.element($document).bind('touchend', dragEndEvent);
+ angular.element($document).bind('touchcancel', dragEndEvent);
+ angular.element($document).bind('touchmove', dragMoveEvent);
+ angular.element($document).bind('mouseup', dragEndEvent);
+ angular.element($document).bind('mousemove', dragMoveEvent);
+ angular.element($document).bind('mouseleave', dragCancelEvent);
+ };
+
+ /**
+ * Unbinds mouse/touch events that handle moving/dropping this dragged node
+ */
+ unbindDragMoveEvents = function () {
+ angular.element($document).unbind('touchend', dragEndEvent);
+ angular.element($document).unbind('touchcancel', dragEndEvent);
+ angular.element($document).unbind('touchmove', dragMoveEvent);
+ angular.element($document).unbind('mouseup', dragEndEvent);
+ angular.element($document).unbind('mousemove', dragMoveEvent);
+ angular.element($document).unbind('mouseleave', dragCancelEvent);
+ };
+
+ keydownHandler = function (e) {
+ if (e.keyCode == 27) {
+ scope.$$allowNodeDrop = false;
+ dragEnd(e);
+ }
+ };
+
+ angular.element($window.document).bind('keydown', keydownHandler);
+
+ //unbind handler that retains scope
+ scope.$on('$destroy', function () {
+ angular.element($window.document).unbind('keydown', keydownHandler);
+ });
+ }
+ };
+ }
+ ]);
+
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+ .directive('uiTreeNodes', ['treeConfig', '$window',
+ function (treeConfig) {
+ return {
+ require: ['ngModel', '?^uiTreeNode', '^uiTree'],
+ restrict: 'A',
+ scope: true,
+ controller: 'TreeNodesController',
+ link: function (scope, element, attrs, controllersArr) {
+
+ var config = {},
+ ngModel = controllersArr[0],
+ treeNodeCtrl = controllersArr[1],
+ treeCtrl = controllersArr[2];
+
+ angular.extend(config, treeConfig);
+ if (config.nodesClass) {
+ element.addClass(config.nodesClass);
+ }
+
+ if (treeNodeCtrl) {
+ treeNodeCtrl.scope.$childNodesScope = scope;
+ scope.$nodeScope = treeNodeCtrl.scope;
+ } else {
+ // find the root nodes if there is no parent node and have a parent ui-tree
+ treeCtrl.scope.$nodesScope = scope;
+ }
+ scope.$treeScope = treeCtrl.scope;
+
+ if (ngModel) {
+ ngModel.$render = function () {
+ scope.$modelValue = ngModel.$modelValue;
+ };
+ }
+
+ scope.$watch(function () {
+ return attrs.maxDepth;
+ }, function (val) {
+ if ((typeof val) == 'number') {
+ scope.maxDepth = val;
+ }
+ });
+
+ scope.$watch(function () {
+ return attrs.nodropEnabled;
+ }, function (newVal) {
+ if ((typeof newVal) != 'undefined') {
+ scope.nodropEnabled = true;
+ }
+ }, true);
+
+ attrs.$observe('horizontal', function (val) {
+ scope.horizontal = ((typeof val) != 'undefined');
+ });
+
+ }
+ };
+ }
+ ]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.tree')
+
+ /**
+ * @ngdoc service
+ * @name ui.tree.service:UiTreeHelper
+ * @requires ng.$document
+ * @requires ng.$window
+ *
+ * @description
+ * angular-ui-tree.
+ */
+ .factory('UiTreeHelper', ['$document', '$window', 'treeConfig',
+ function ($document, $window, treeConfig) {
+ return {
+
+ /**
+ * A hashtable used to storage data of nodes
+ * @type {Object}
+ */
+ nodesData: {},
+
+ setNodeAttribute: function (scope, attrName, val) {
+ if (!scope.$modelValue) {
+ return null;
+ }
+ var data = this.nodesData[scope.$modelValue.$$hashKey];
+ if (!data) {
+ data = {};
+ this.nodesData[scope.$modelValue.$$hashKey] = data;
+ }
+ data[attrName] = val;
+ },
+
+ getNodeAttribute: function (scope, attrName) {
+ if (!scope.$modelValue) {
+ return null;
+ }
+ var data = this.nodesData[scope.$modelValue.$$hashKey];
+ if (data) {
+ return data[attrName];
+ }
+ return null;
+ },
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.tree.service:$nodrag
+ * @param {Object} targetElm angular element
+ * @return {Bool} check if the node can be dragged.
+ */
+ nodrag: function (targetElm) {
+ if (typeof targetElm.attr('data-nodrag') != 'undefined') {
+ return targetElm.attr('data-nodrag') !== 'false';
+ }
+ return false;
+ },
+
+ /**
+ * get the event object for touches
+ * @param {[type]} e [description]
+ * @return {[type]} [description]
+ */
+ eventObj: function (e) {
+ var obj = e;
+ if (e.targetTouches !== undefined) {
+ obj = e.targetTouches.item(0);
+ } else if (e.originalEvent !== undefined && e.originalEvent.targetTouches !== undefined) {
+ obj = e.originalEvent.targetTouches.item(0);
+ }
+ return obj;
+ },
+
+ dragInfo: function (node) {
+ return {
+ source: node,
+ sourceInfo: {
+ cloneModel: node.$treeScope.cloneEnabled === true ? angular.copy(node.$modelValue) : undefined,
+ nodeScope: node,
+ index: node.index(),
+ nodesScope: node.$parentNodesScope
+ },
+ index: node.index(),
+ siblings: node.siblings().slice(0),
+ parent: node.$parentNodesScope,
+
+ // Move the node to a new position
+ moveTo: function (parent, siblings, index) {
+ this.parent = parent;
+ this.siblings = siblings.slice(0);
+
+ // If source node is in the target nodes
+ var i = this.siblings.indexOf(this.source);
+ if (i > -1) {
+ this.siblings.splice(i, 1);
+ if (this.source.index() < index) {
+ index--;
+ }
+ }
+
+ this.siblings.splice(index, 0, this.source);
+ this.index = index;
+ },
+
+ parentNode: function () {
+ return this.parent.$nodeScope;
+ },
+
+ prev: function () {
+ if (this.index > 0) {
+ return this.siblings[this.index - 1];
+ }
+
+ return null;
+ },
+
+ next: function () {
+ if (this.index < this.siblings.length - 1) {
+ return this.siblings[this.index + 1];
+ }
+
+ return null;
+ },
+
+ isClone: function () {
+ return this.source.$treeScope.cloneEnabled === true;
+ },
+
+ clonedNode: function (node) {
+ return angular.copy(node);
+ },
+
+ isDirty: function () {
+ return this.source.$parentNodesScope != this.parent ||
+ this.source.index() != this.index;
+ },
+
+ isForeign: function () {
+ return this.source.$treeScope !== this.parent.$treeScope;
+ },
+
+ eventArgs: function (elements, pos) {
+ return {
+ source: this.sourceInfo,
+ dest: {
+ index: this.index,
+ nodesScope: this.parent
+ },
+ elements: elements,
+ pos: pos
+ };
+ },
+
+ apply: function () {
+
+ var nodeData = this.source.$modelValue;
+
+ // nodrop enabled on tree or parent
+ if (this.parent.nodropEnabled || this.parent.$treeScope.nodropEnabled) {
+ return;
+ }
+
+ // node was dropped in the same place - do nothing
+ if (!this.isDirty()) {
+ return;
+ }
+
+ // cloneEnabled and cross-tree so copy and do not remove from source
+ if (this.isClone() && this.isForeign()) {
+ this.parent.insertNode(this.index, this.sourceInfo.cloneModel);
+ } else { // Any other case, remove and reinsert
+ this.source.remove();
+ this.parent.insertNode(this.index, nodeData);
+ }
+ }
+ };
+ },
+
+ /**
+ * @ngdoc method
+ * @name ui.tree#height
+ * @methodOf ui.tree.service:UiTreeHelper
+ *
+ * @description
+ * Get the height of an element.
+ *
+ * @param {Object} element Angular element.
+ * @returns {String} Height
+ */
+ height: function (element) {
+ return element.prop('scrollHeight');
+ },
+
+ /**
+ * @ngdoc method
+ * @name ui.tree#width
+ * @methodOf ui.tree.service:UiTreeHelper
+ *
+ * @description
+ * Get the width of an element.
+ *
+ * @param {Object} element Angular element.
+ * @returns {String} Width
+ */
+ width: function (element) {
+ return element.prop('scrollWidth');
+ },
+
+ /**
+ * @ngdoc method
+ * @name ui.tree#offset
+ * @methodOf ui.nestedSortable.service:UiTreeHelper
+ *
+ * @description
+ * Get the offset values of an element.
+ *
+ * @param {Object} element Angular element.
+ * @returns {Object} Object with properties width, height, top and left
+ */
+ offset: function (element) {
+ var boundingClientRect = element[0].getBoundingClientRect();
+
+ return {
+ width: element.prop('offsetWidth'),
+ height: element.prop('offsetHeight'),
+ top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
+ left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
+ };
+ },
+
+ /**
+ * @ngdoc method
+ * @name ui.tree#positionStarted
+ * @methodOf ui.tree.service:UiTreeHelper
+ *
+ * @description
+ * Get the start position of the target element according to the provided event properties.
+ *
+ * @param {Object} e Event
+ * @param {Object} target Target element
+ * @returns {Object} Object with properties offsetX, offsetY, startX, startY, nowX and dirX.
+ */
+ positionStarted: function (e, target) {
+ var pos = {},
+ pageX = e.pageX,
+ pageY = e.pageY;
+
+ if (e.originalEvent && e.originalEvent.touches && (e.originalEvent.touches.length > 0)) {
+ pageX = e.originalEvent.touches[0].pageX;
+ pageY = e.originalEvent.touches[0].pageY;
+ }
+ pos.offsetX = pageX - this.offset(target).left;
+ pos.offsetY = pageY - this.offset(target).top;
+ pos.startX = pos.lastX = pageX;
+ pos.startY = pos.lastY = pageY;
+ pos.nowX = pos.nowY = pos.distX = pos.distY = pos.dirAx = 0;
+ pos.dirX = pos.dirY = pos.lastDirX = pos.lastDirY = pos.distAxX = pos.distAxY = 0;
+ return pos;
+ },
+
+ positionMoved: function (e, pos, firstMoving) {
+ var pageX = e.pageX,
+ pageY = e.pageY,
+ newAx;
+ if (e.originalEvent && e.originalEvent.touches && (e.originalEvent.touches.length > 0)) {
+ pageX = e.originalEvent.touches[0].pageX;
+ pageY = e.originalEvent.touches[0].pageY;
+ }
+ // mouse position last events
+ pos.lastX = pos.nowX;
+ pos.lastY = pos.nowY;
+
+ // mouse position this events
+ pos.nowX = pageX;
+ pos.nowY = pageY;
+
+ // distance mouse moved between events
+ pos.distX = pos.nowX - pos.lastX;
+ pos.distY = pos.nowY - pos.lastY;
+
+ // direction mouse was moving
+ pos.lastDirX = pos.dirX;
+ pos.lastDirY = pos.dirY;
+
+ // direction mouse is now moving (on both axis)
+ pos.dirX = pos.distX === 0 ? 0 : pos.distX > 0 ? 1 : -1;
+ pos.dirY = pos.distY === 0 ? 0 : pos.distY > 0 ? 1 : -1;
+
+ // axis mouse is now moving on
+ newAx = Math.abs(pos.distX) > Math.abs(pos.distY) ? 1 : 0;
+
+ // do nothing on first move
+ if (firstMoving) {
+ pos.dirAx = newAx;
+ pos.moving = true;
+ return;
+ }
+
+ // calc distance moved on this axis (and direction)
+ if (pos.dirAx !== newAx) {
+ pos.distAxX = 0;
+ pos.distAxY = 0;
+ } else {
+ pos.distAxX += Math.abs(pos.distX);
+ if (pos.dirX !== 0 && pos.dirX !== pos.lastDirX) {
+ pos.distAxX = 0;
+ }
+
+ pos.distAxY += Math.abs(pos.distY);
+ if (pos.dirY !== 0 && pos.dirY !== pos.lastDirY) {
+ pos.distAxY = 0;
+ }
+ }
+
+ pos.dirAx = newAx;
+ },
+
+ elementIsTreeNode: function (element) {
+ return typeof element.attr('ui-tree-node') !== 'undefined';
+ },
+
+ elementIsTreeNodeHandle: function (element) {
+ return typeof element.attr('ui-tree-handle') !== 'undefined';
+ },
+ elementIsTree: function (element) {
+ return typeof element.attr('ui-tree') !== 'undefined';
+ },
+ elementIsTreeNodes: function (element) {
+ return typeof element.attr('ui-tree-nodes') !== 'undefined';
+ },
+ elementIsPlaceholder: function (element) {
+ return element.hasClass(treeConfig.placeholderClass);
+ },
+ elementContainsTreeNodeHandler: function (element) {
+ return element[0].querySelectorAll('[ui-tree-handle]').length >= 1;
+ },
+ treeNodeHandlerContainerOfElement: function (element) {
+ return findFirstParentElementWithAttribute('ui-tree-handle', element[0]);
+ }
+ };
+ }
+ ]);
+
+ // TODO: optimize this loop
+ function findFirstParentElementWithAttribute(attributeName, childObj) {
+ // undefined if the mouse leaves the browser window
+ if (childObj === undefined) {
+ return null;
+ }
+ var testObj = childObj.parentNode,
+ count = 1,
+ // check for setAttribute due to exception thrown by Firefox when a node is dragged outside the browser window
+ res = (typeof testObj.setAttribute === 'function' && testObj.hasAttribute(attributeName)) ? testObj : null;
+ while (testObj && typeof testObj.setAttribute === 'function' && !testObj.hasAttribute(attributeName)) {
+ testObj = testObj.parentNode;
+ res = testObj;
+ if (testObj === document.documentElement) {
+ res = null;
+ break;
+ }
+ count++;
+ }
+
+ return res;
+ }
+
+})();
|