<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8" />
    <title>AngularJS UI Tree demo</title>
    <script data-require="underscore.js@1.6.0" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
    <link rel="stylesheet" href="http://jimliu.github.io/angular-ui-tree/dist/angular-ui-tree.min.css" />
    <link rel="stylesheet" href="demo.css" />
  </head>

  <body ng-app="demoApp">
    <div class="container">
      <div class="row">
        <div class="col-md-12">
          <div class="row" ng-controller="MainCtrl">
            <div class="col-lg-6">
              <h1>Hierarchy Selector</h1>
              <hr>
              <hierarchy-search dataset="list"></hierarchy-search>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- JavaScript -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
    <script src="ui-tree.js"></script>
    <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.js"></script>
    <script src="demolist.js"></script>
    <script src="HierarchyNodeService.js"></script>
    <script src="hierarchySearch.js"></script>
    <script src="demo.js"></script>
  </body>

</html>
(function() {
    console.clear();
'use strict';
angular.module('demoApp', ['ui.tree', 'ui.bootstrap','demoApp.list','demoApp.directives','demoApp.services'])

.controller('MainCtrl', function($scope,$timeout,MyList,HierarchyNodeService) {
  
    //console.log(HierarchyNodeService);
    $scope.baseList = MyList;
    $scope.list = $scope.baseList;   
  
})
.directive('indeterminateCheckbox',function(HierarchyNodeService) {
    return {
        restrict:'A',
        scope: {
          node:'='  
        },
        link: function(scope, element, attr) {
            
            scope.$watch('node',function(nv) {
                
                var flattenedTree = HierarchyNodeService.getAllChildren(scope.node,[]);
                flattenedTree = flattenedTree.map(function(n){ return n.isSelected });
                var initalLength = flattenedTree.length;
                var compactedTree = _.compact(flattenedTree);
                
                var r = compactedTree.length > 0 && compactedTree.length < flattenedTree.length;
                element.prop('indeterminate', r);
                
            },true);
            
        }
    }
})

})();
* {
  -webkit-font-smoothing: antialiased;
}
.noselect {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.past-users-filter {
    cursor:pointer;
    margin-left:20px;
}
.no-results {
  padding-left:20px;
  padding-bottom:10px;
}

/*matched result from search*/
.matched {
    font-weight:700 !important;
}

/*wraps entire selector*/
.hierarchy-wrap {
    background: #FFFFFF;
    border:1px solid #ccc;
    border-radius:5px;
}
/* past users + search */
.hierarchy-header {
    padding:10px;
}

.hierarchy-header .search-wrap {
    margin-bottom:5px;
}

.angular-ui-tree-handle {
    color: black;
    padding: 5px;
    font-weight:normal;
    cursor:pointer;
}

.angular-ui-tree-nodes .angular-ui-tree-nodes { padding-left:0; }
.angular-ui-tree-nodes .angular-ui-tree-nodes .node-embed {
    padding-left:10px;
}
.angular-ui-tree-nodes .angular-ui-tree-nodes .angular-ui-tree-nodes .node-embed {
    padding-left:20px;
}

.angular-ui-tree-nodes .angular-ui-tree-nodes .angular-ui-tree-nodes .angular-ui-tree-nodes .node-embed {
    padding-left:30px;
}


.angular-ui-tree-handle:hover {
    color: black;
    background: #f5f5f5;
}

.node-header {
    padding-right:5px;
}

.nodeActive {
    background:#deeaf9;
}

.nodeActive:hover {
    background: #428bca;
    color:#FEFEFA;
}

.angular-ui-tree-handle i {
    color:black;
}


/**
 * @license Angular UI Tree v2.1.5
 * (c) 2010-2014. https://github.com/JimLiu/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
    });

})();

(function () {
  'use strict';

  angular.module('ui.tree')

   /**
    * @ngdoc service
    * @name ui.tree.service:$helper
    * @requires ng.$document
    * @requires ng.$window
    *
    * @description
    * angular-ui-tree.
    */
    .factory('$uiTreeHelper', ['$document', '$window',
      function ($document, $window) {
        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) {
            return (typeof targetElm.attr('data-nodrag')) != "undefined";
          },

          /**
           * get the event object for touchs
           * @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: {
                nodeScope: node,
                index: node.index(),
                nodesScope: node.$parentNodesScope
              },
              index: node.index(),
              siblings: node.siblings().slice(0),
              parent: node.$parentNodesScope,

              moveTo: function(parent, siblings, index) { // Move the node to a new position
                this.parent = parent;
                this.siblings = siblings.slice(0);
                var i = this.siblings.indexOf(this.source); // If source node is in the target nodes
                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;
              },

              isDirty: function() {
                return this.source.$parentNodesScope != this.parent ||
                        this.source.index() != this.index;
              },

              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;
                this.source.remove();
                this.parent.insertNode(this.index, nodeData);
              }
            };
          },

          /**
          * @ngdoc method
          * @name hippo.theme#height
          * @methodOf ui.tree.service:$helper
          *
          * @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 hippo.theme#width
          * @methodOf ui.tree.service:$helper
          *
          * @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 hippo.theme#offset
          * @methodOf ui.nestedSortable.service:$helper
          *
          * @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 hippo.theme#positionStarted
          * @methodOf ui.tree.service:$helper
          *
          * @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 = {};
            pos.offsetX = e.pageX - this.offset(target).left;
            pos.offsetY = e.pageY - this.offset(target).top;
            pos.startX = pos.lastX = e.pageX;
            pos.startY = pos.lastY = e.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) {
            // mouse position last events
            pos.lastX = pos.nowX;
            pos.lastY = pos.nowY;

            // mouse position this events
            pos.nowX  = e.pageX;
            pos.nowY  = e.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
            var 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;
          }
        };
      }
    ]);

})();
(function () {
  'use strict';

  angular.module('ui.tree')

    .controller('TreeController', ['$scope', '$element', '$attrs', 'treeConfig',
      function ($scope, $element, $attrs, treeConfig) {
        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;

        // 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();
        };

        $scope.resetEmptyElement = function() {
          if ($scope.$nodesScope.$modelValue.length === 0 &&
            $scope.emptyPlaceHolderEnabled) {
            $element.append($scope.$emptyElm);
          } else {
            $scope.$emptyElm.remove();
          }
        };

        var collapseOrExpand = function(scope, collapsed) {
          var nodes = scope.childNodes();
          for (var i = 0; i < nodes.length; i++) {
            collapsed ? nodes[i].collapse() : nodes[i].expand();
            var subScope = nodes[i].$childNodesScope;
            if (subScope) {
              collapseOrExpand(subScope, collapsed);
            }
          }
        };

        $scope.collapseAll = function() {
          collapseOrExpand($scope.$nodesScope, true);
        };

        $scope.expandAll = function() {
          collapseOrExpand($scope.$nodesScope, false);
        };

      }
    ]);
})();

(function () {
  'use strict';

  angular.module('ui.tree')

    .controller('TreeNodesController', ['$scope', '$element', 'treeConfig',
      function ($scope, $element, treeConfig) {
        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.nodrop = false;
        $scope.maxDepth = 0;

        $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 node;
          }
          return null;
        };

        $scope.insertNode = function(index, nodeData) {
          $scope.safeApply(function() {
            $scope.$modelValue.splice(index, 0, nodeData);
          });
        };

        $scope.childNodes = function() {
          var nodes = [];
          if ($scope.$modelValue) {
            for (var 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('TreeNodeController', ['$scope', '$element', '$attrs', 'treeConfig',
      function ($scope, $element, $attrs, treeConfig) {
        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.$$apply = false; //

        $scope.collapsed = 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
          try{$scope.$modelValue = treeNodesCtrl.scope.$modelValue[$scope.$index];} catch(e){}
          $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.removeNode = function(){
          var node = $scope.remove();
          $scope.$callbacks.removed(node);
          return node;
        };

        $scope.remove = function() {
          return $scope.$parentNodesScope.removeNode($scope);
        };

        $scope.toggle = function() {
          $scope.collapsed = !$scope.collapsed;
        };

        $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;
        };

        var subDepth = 0;
        var countSubDepth = function(scope) {
          var count = 0;
          var nodes = scope.childNodes();
          for (var i = 0; i < nodes.length; i++) {
            var childNodes = nodes[i].$childNodesScope;
            if (childNodes) {
              count = 1;
              countSubDepth(childNodes);
            }
          }
          subDepth += count;
        };

        $scope.maxSubDepth = function() {
          subDepth = 0;
          if ($scope.$childNodesScope) {
            countSubDepth($scope.$childNodesScope);
          }
          return subDepth;
        };

      }
    ]);
})();

(function () {
  'use strict';

  angular.module('ui.tree')

    .controller('TreeHandleController', ['$scope', '$element', '$attrs', 'treeConfig',
      function ($scope, $element, $attrs, treeConfig) {
        this.scope = $scope;

        $scope.$element = $element;
        $scope.$nodeScope = null;
        $scope.$type = 'uiTreeHandle';

      }
    ]);
})();

(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) {
          var callbacks = {
            accept: null,
            beforeDrag: null
          };

          var config = {};
          angular.extend(config, treeConfig);
          if (config.treeClass) {
            element.addClass(config.treeClass);
          }

          scope.$emptyElm = angular.element($window.document.createElement('div'));
          if (config.emptyTreeClass) {
            scope.$emptyElm.addClass(config.emptyTreeClass);
          }

          scope.$watch('$nodesScope.$modelValue.length', function() {
            if (scope.$nodesScope.$modelValue) {
              scope.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;
            }
          });

          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;
            }
          });

          // check if the dest node can accept the dragging node
          // by default, we check the 'data-nodrop' attribute in `ui-tree-nodes`
          // and the 'max-depth' attribute in `ui-tree` or `ui-tree-nodes`.
          // the method can be overrided
          callbacks.accept = function(sourceNodeScope, destNodesScope, destIndex) {
            if (destNodesScope.nodrop || destNodesScope.outOfDepth(sourceNodeScope)) {
              return false;
            }
            return true;
          };

          callbacks.beforeDrag = function(sourceNodeScope) {
            return true;
          };

          callbacks.removed = function(node){
          
          };

          callbacks.dropped = function(event) {

          };

          //
          callbacks.dragStart = function(event) {

          };

          callbacks.dragMove = function(event) {

          };

          callbacks.dragStop = function(event) {

          };

          callbacks.beforeDrop = function(event) {

          };

          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('uiTreeNodes', [ 'treeConfig', '$window',
    function(treeConfig) {
      return {
        require: ['ngModel', '?^uiTreeNode', '^uiTree'],
        restrict: 'A',
        scope: true,
        controller: 'TreeNodesController',
        link: function(scope, element, attrs, controllersArr) {

          var config = {};
          angular.extend(config, treeConfig);
          if (config.nodesClass) {
            element.addClass(config.nodesClass);
          }

          var ngModel = controllersArr[0];
          var treeNodeCtrl = controllersArr[1];
          var treeCtrl = controllersArr[2];
          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() {
              if (!ngModel.$modelValue || !angular.isArray(ngModel.$modelValue)) {
                scope.$modelValue = [];
              }
              scope.$modelValue = ngModel.$modelValue;
            };
          }

          scope.$watch(attrs.maxDepth, function(val) {
            if((typeof val) == "number") {
              scope.maxDepth = val;
            }
          });

          attrs.$observe('nodrop', function(val) {
            scope.nodrop = ((typeof val) != "undefined");
          });

          attrs.$observe('horizontal', function(val) {
            scope.horizontal = ((typeof val) != "undefined");
          });

        }
      };
    }
  ]);
})();

(function () {
  'use strict';

  angular.module('ui.tree')

    .directive('uiTreeNode', ['treeConfig', '$uiTreeHelper', '$window', '$document','$timeout',
      function (treeConfig, $uiTreeHelper, $window, $document, $timeout) {
        return {
          require: ['^uiTreeNodes', '^uiTree'],
          restrict: 'A',
          controller: 'TreeNodeController',
          link: function(scope, element, attrs, controllersArr) {
            var config = {};
            angular.extend(config, treeConfig);
            if (config.nodeClass) {
              element.addClass(config.nodeClass);
            }
            scope.init(controllersArr);

            scope.collapsed = !!$uiTreeHelper.getNodeAttribute(scope, 'collapsed');

            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);
            });

            var hasTouch = 'ontouchstart' in window;
            // todo startPos is unused
            var startPos, firstMoving, dragInfo, pos;
            var placeElm, hiddenPlaceElm, dragElm;
            var treeScope = null;
            var elements; // As a parameter for callbacks
            var dragDelaying = true;
            var dragStarted = false;
            var dragTimer = null;
            var body = document.body,
                html = document.documentElement,
                document_height,
                document_width;

            var dragStart = function(e) {
              if (!hasTouch && (e.button == 2 || e.which == 3)) {
                // disable right click
                return;
              }
              if (e.uiTreeDragging || (e.originalEvent && e.originalEvent.uiTreeDragging)) { // event has already fired in other scope.
                return;
              }

              // the element which is clicked.
              var eventElm = angular.element(e.target);
              var eventScope = eventElm.scope();
              if (!eventScope || !eventScope.$type) {
                return;
              }
              if (eventScope.$type != 'uiTreeNode'
                && eventScope.$type != 'uiTreeHandle') { // Check if it is a node or a handle
                return;
              }
              if (eventScope.$type == 'uiTreeNode'
                && eventScope.$handleScope) { // If the node has a handle, then it should be clicked by the handle
                return;
              }

              var 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
              while (eventElm && eventElm[0] && eventElm[0] != element) {
                if ($uiTreeHelper.nodrag(eventElm)) { // if the node mark as `nodrag`, DONOT drag it.
                  return;
                }
                eventElm = eventElm.parent();
              }

              if (!scope.beforeDrag(scope)){
                return;
              }

              e.uiTreeDragging = true; // stop event bubbling
              if (e.originalEvent) {
                e.originalEvent.uiTreeDragging = true;
              }
              e.preventDefault();
              var eventObj = $uiTreeHelper.eventObj(e);

              firstMoving = true;
              dragInfo = $uiTreeHelper.dragInfo(scope);

              var tagName = scope.$element.prop('tagName');
              if (tagName.toLowerCase() === 'tr') {
                placeElm = angular.element($window.document.createElement(tagName));
                var tdElm = angular.element($window.document.createElement('td'))
                              .addClass(config.placeHolderClass);
                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, scope.$element);
              placeElm.css('height', $uiTreeHelper.height(scope.$element) + 'px');
              placeElm.css('width', $uiTreeHelper.width(scope.$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(scope.$element) + 'px');
              dragElm.css('z-index', 9999);

              // Prevents cursor to change rapidly in Opera 12.16 and IE when dragging an element
              var hStyle = (scope.$element[0].querySelector('.angular-ui-tree-handle') || scope.$element[0]).currentStyle;
              if (hStyle) {
                document.body.setAttribute('ui-tree-cursor', $document.find('body').css('cursor') || '');
                $document.find('body').css({'cursor': hStyle.cursor + '!important'});
              }

              scope.$element.after(placeElm);
              scope.$element.after(hiddenPlaceElm);
              dragElm.append(scope.$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
              };

              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);

              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);
            };

            var dragMove = function(e) {
              if (!dragStarted) {
                if (!dragDelaying) {
                  dragStarted = true;
                  scope.$apply(function() {
                    scope.$callbacks.dragStart(dragInfo.eventArgs(elements, pos));
                  });
                }
                return;
              }

              var eventObj = $uiTreeHelper.eventObj(e);
              var prev, leftElmPos, topElmPos;

              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'
                });

                var top_scroll = window.pageYOffset || $window.document.documentElement.scrollTop;
                var 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) {
                  window.scrollBy(0, 10);
                }

                // 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;
                }

                // 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
                    var next = dragInfo.next();
                    if (!next) {
                      var 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);
                      }
                    }
                  }
                }

                // check if add it as a child node first
                // todo decrease is unused
                var decrease = ($uiTreeHelper.offset(dragElm).left - $uiTreeHelper.offset(placeElm).left) >= config.threshold;
                var targetX = eventObj.pageX - $window.document.body.scrollLeft;
                var targetY = eventObj.pageY - (window.pageYOffset || $window.document.documentElement.scrollTop);

                // 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.
                var displayElm;
                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);

                var targetElm = angular.element($window.document.elementFromPoint(targetX, targetY));
                if (angular.isFunction(dragElm.show)) {
                  dragElm.show();
                }else{
                  dragElm[0].style.display = displayElm;
                }

                // move vertical
                if (!pos.dirAx) {
                  var targetBefore, targetNode;
                  // check it's new position
                  targetNode = targetElm.scope();
                  var isEmpty = false;
                  if (!targetNode) {
                    return;
                  }
                  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
                    targetElm = targetNode.$element; // Get the element of ui-tree-node
                    var 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());
                    }
                  }

                }

                scope.$apply(function() {
                  scope.$callbacks.dragMove(dragInfo.eventArgs(elements, pos));
                });
              }
            };

            var dragEnd = function(e) {
              e.preventDefault();

              if (dragElm) {
                scope.$treeScope.$apply(function() {
                  scope.$callbacks.beforeDrop(dragInfo.eventArgs(elements, pos));
                });
                // roll back elements changed
                hiddenPlaceElm.replaceWith(scope.$element);
                placeElm.remove();

                dragElm.remove();
                dragElm = null;
                if (scope.$$apply) {
                  dragInfo.apply();
                  scope.$treeScope.$apply(function() {
                    scope.$callbacks.dropped(dragInfo.eventArgs(elements, pos));
                  });
                } else {
                  bindDrag();
                }
                scope.$treeScope.$apply(function() {
                  scope.$callbacks.dragStop(dragInfo.eventArgs(elements, pos));
                });
                scope.$$apply = 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');
              }

              angular.element($document).unbind('touchend', dragEndEvent); // Mobile
              angular.element($document).unbind('touchcancel', dragEndEvent); // Mobile
              angular.element($document).unbind('touchmove', dragMoveEvent); // Mobile
              angular.element($document).unbind('mouseup', dragEndEvent);
              angular.element($document).unbind('mousemove', dragMoveEvent);
              angular.element($window.document.body).unbind('mouseleave', dragCancelEvent);
            };

            var dragStartEvent = function(e) {
              if (scope.dragEnabled()) {
                dragStart(e);
              }
            };

            var dragMoveEvent = function(e) {
              dragMove(e);
            };

            var dragEndEvent = function(e) {
              scope.$$apply = true;
              dragEnd(e);
            };

            var dragCancelEvent = function(e) {
              dragEnd(e);
            };

            var bindDrag = function() {
              element.bind('touchstart mousedown', function (e) {
                dragDelaying = true;
                dragStarted = false;
                dragStartEvent(e);
                dragTimer = $timeout(function(){dragDelaying = false;}, scope.dragDelay);
              });
              element.bind('touchend touchcancel mouseup',function(){$timeout.cancel(dragTimer);});
            };
            bindDrag();

            angular.element($window.document.body).bind("keydown", function(e) {
              if (e.keyCode == 27) {
                scope.$$apply = false;
                dragEnd(e);
              }
            });
          }
        };
      }
    ]);

})();

(function () {
  'use strict';

  angular.module('ui.tree')
  .directive('uiTreeHandle', [ 'treeConfig', '$window',
    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;
          }
        }
      };
    }
  ]);
})();
<div class="node-embed" ui-tree-handle ng-click="itemSelect(item,this)" ng-class="{nodeActive:item.isSelected,matched:item.match}">
    <a class="btn btn-clear btn-xs" data-ng-show="item.items.length" ng-click="expandNode(this,$event)">
        <i class="fa" ng-class="{'fa-chevron-right': collapsed, 'fa-chevron-down': !collapsed}"></i>
    </a>
    <span class="node-header">
        <input type="checkbox" ng-checked="item.isSelected" indeterminate-checkbox node="item" />
    </span>
    {{item.title}}
    
</div>

<ol ui-tree-nodes="options" ng-model="item.items" ng-class="{hidden: collapsed,displayed:!collapsed}">
  <li ng-repeat="item in item.items" ui-tree-node collapsed="true" ng-include="'items_renderer.html'">
  </li>
</ol>
(function() {
angular.module('demoApp.list',[])
.value('MyList',[
{
  "id": 1,
  "title": "ASD Headquarters",
  "items": [
    {
      "id": 11,
      "title": "San Jose",
      "items": [
        {
         "id":13,
         "title":"Jensen Chapman's Team",
         "items": [
            {
              "id":14,
              "title":"Jimmy John"
            },
            {
              "id":15,
              "title":"Daniel Mills"
            }
            ,
            {
              "id":16,
              "title":"Chris Boden"
            }
           ]
        }
        ],
    },
    {
      "id": 12,
      "title": "Irvine",
      "items": [
        {
         "id":23,
         "title":"Tracey Chapman's Team",
         "items": [
            {
              "id":24,
              "title":"San Jesus"
            },
            {
              "id":25,
              "title":"Fat Albert"
            }
            ,
            {
              "id":26,
              "title":"Connor McDavid"
            }
           ]
        }
        ]
    },
    {
      "id":30,
      "title":"San Diego",
      "items": [{
        "id":31,
        "title":"Duran Duran's Team",
        "items":[
             {
              "id":32,
              "title":"Amberlynn Pinkerton"
            },
            {
              "id":33,
              "title":"Tony Mejia"
            }
            ,
            {
              "id":34,
              "title":"Richard Partridge"
            }
            ,
            {
              "id":35,
              "title":"Elliot Stabler"
            }
          ]
        },
        {
        "id":40,
        "title":"Steely Dan's Team",
        "items":[
             {
              "id":36,
              "title":"Tony Stark"
            },
            {
              "id":37,
              "title":"Totally Rad"
            }
            ,
            {
              "id":38,
              "title":"Matt Murdock"
            }
            ,
            {
              "id":39,
              "title":"Stan Lee"
            }
          ]
        }
      ]
    }
  ]
}
]);

})()
<div>
    <div class="hierarchy-wrap">
        <div class="hierarchy-header">
            <div class="noselect">
                <label for="pastUsersFilter" class=" past-users-filter">
                    <input type="checkbox"  ng-model="pastUsersFilter" id="pastUsersFilter"/>
                    <span style="margin-left:5px">Include Past Users</span>
                </label>
                <div class="pull-right">
                  <strong>{{numSelected}} selected</strong>
                </div>
            </div>
            <div class="input-group search-wrap">
                <div class="input-group-addon"><i class="fa fa-search"></i></div>
                <input type="text" class="form-control" ng-model="searchValue" placeholder="Search..." />
            </div>
        </div>
        <div class="no-results" ng-show="emptyData"><strong>No Results Found</strong></div>
        <div ng-show="!emptyData" ui-tree="options" data-drag-enabled="false">
            <ol id="demoTree" ui-tree-nodes="" ng-model="list">
              <li ng-repeat="item in list" ui-tree-node collapsed="true" ng-include="'items_renderer.html'"></li>
            </ol>
        </div>
    </div>
    <hr>
    <h2>Selected Ids</h2>
    <pre class="code">{{ selected }}</pre>
    <h2>Search Results</h2>
    <pre class="code">{{ list | json }}</pre>
</div>
(function(){
    angular.module('demoApp.directives',[])
    .directive('hierarchySearch',function(HierarchyNodeService,$timeout) {
    
    return {
        restrict:'E',
        templateUrl:'hierarchySearch.tpl.html',
        scope: {
            dataset:'='
        },
        controller:function($scope) {
            $scope.numSelected = 0;
            //$scope.list is used by ng-tree, dataset should never be modified
            $scope.list = angular.copy($scope.dataset);
            
            $scope.options = {};
            
            $scope.expandNode = function(n,$event) {
                $event.stopPropagation();
                n.toggle();
            }
            
            
            $scope.itemSelect = function(item) {
                var rootVal = !item.isSelected;
                HierarchyNodeService.selectChildren(item,rootVal)
                
                HierarchyNodeService.findParent($scope.list[0],null,item,selectParent);
                var s = _.compact(HierarchyNodeService.getAllChildren($scope.list[0],[]).map(function(c){ return c.isSelected && !c.items;}));
                $scope.numSelected = s.length;
            }   
            
            function selectParent(parent) {
                var children = HierarchyNodeService.getAllChildren(parent,[]);
                
                if(!children) return;
                children = children.slice(1).map(function(c){ return c.isSelected;});
                
                parent.isSelected =  children.length === _.compact(children).length;
                HierarchyNodeService.findParent($scope.list[0],null,parent,selectParent)
            }       

            $scope.nodeStatus = function(node) {
                var flattenedTree = getAllChildren(node,[]);
                flattenedTree = flattenedTree.map(function(n){ return n.isSelected });
    
                return flattenedTree.length === _.compact(flattenedTree);
            }  
 
        },
        link:function(scope,el,attr) {
            
            scope.$watch('pastUsersFilter',function(nv){
               if(_.isUndefined(nv)) return;
               
               if(nv) {
                   HierarchyNodeService.trimLeafs(scope.list[0]);
               } else {
                   scope.list = angular.copy(scope.dataset);
               }
               
            });
            
          
            var inputTimeout;
            var time = 300;
            scope.$watch('searchValue',function(nv) {
                if(!nv && nv !== '') {
                    return;
                }
                var previousDataset = angular.copy(scope.list);
                var newData = (scope.searchValue === '') ? angular.copy(scope.dataset) : [HierarchyNodeService.treeSearch(angular.copy(scope.dataset[0]),scope.searchValue)];
                
                if(newData.length === 1 && _.isEmpty(newData[0]) ) {
                  scope.emptyData = true;
                  return;
                }
                
                scope.emptyData = false;
                if(_.isEqual(previousDataset,newData)) {
                  clearTimeout(inputTimeout);
                  return;
                } 
                
                scope.list = newData;
                
                
                $timeout.cancel(inputTimeout);
                inputTimeout = $timeout(function() {
                    
                    var els = document.querySelectorAll('[ui-tree-node]');
                    
                    Array.prototype.forEach.call(els,function(el) {
                        el = angular.element(el);
                        var elScope = el.scope();
                        if(elScope.$modelValue.match) {
                            
                            elScope.expand();
                            
                            //loop through all parents and keep expanding until no more parents are found
                            var p = elScope.$parentNodeScope;
                            while(p) {
                              p.expand();
                              p = p.$parentNodeScope;
                              
                            }
                        }
                    });
                },500);
            });
            
            
          
            
            scope.$watch('list',function(nv,ov) {
                if(!nv) return;
                if(nv && !ov) { scope.$apply();}
                
                
                //UPDATE SELECTED IDs FOR QUERY
                //get the root node
                var rootNode = nv[0];
                
                //get all elements where isSelected == true
                var a = HierarchyNodeService.getSelected(rootNode,[]);
                
                //get the ids of each element
                a = _.pluck(a,'id');
                
                scope.selected = a;
        
            },true);
        }
    }
})
}).call(this);
(function(){

angular.module('demoApp.services',[])
.service('HierarchyNodeService',function() {

    function lowerCase(str) {
        return str.split(' ').map(function(e){
            return e.toString().toLowerCase();  
        }).join(' ');
    }
    
    function treeSearch(tree, query) {
        if (!tree) {
            return {};
        }

        if (lowerCase(tree.title).indexOf(lowerCase(query)) > -1) {
            tree.match = true;
            return tree;
        }

        var branches = _.reduce(tree.items, function(acc, leaf) {
            var newLeaf = treeSearch(leaf, query);

            if (!_.isEmpty(newLeaf)) {
                acc.push(newLeaf);
            }

            return acc;
        }, []);

        if (_.size(branches) > 0) {
            var trunk = _.omit(tree, 'items');
            trunk.items = branches;
        
            return trunk;
        }
    
        return {};
    }   
    
   function getAllChildren(node,arr) {
       if(!node) return;
        arr.push(node);

        if(node.items) {
            //if the node has children call getSelected for each and concat to array
            node.items.forEach(function(childNode) {
                arr = arr.concat(getAllChildren(childNode,[]))  
            })
        }
        return arr;   
    }    
    

    
    function findParent(node,parent,targetNode,cb) {
        if(_.isEqual(node,targetNode)) {
            cb(parent);
            return;
        }
        
        if(node.items) {
            node.items.forEach(function(item){
                findParent(item,node,targetNode,cb);
            });
        }
    }
            
    
    function getSelected(node,arr) {
        //if(!node) return [];
        //if this node is selected add to array
        if(node.isSelected) {
            arr.push(node);
            return arr;
        }
        
        if(node.items) {
            //if the node has children call getSelected for each and concat to array
            node.items.forEach(function(childNode) {
                arr = arr.concat(getSelected(childNode,[]))  
            })
        }
        return arr;    
    }
    
    function selectChildren(children,val) {

        //set as selected
        children.isSelected = val;
        if(children.items) {
            //recursve to set all children as selected
            children.items.forEach(function(el) {
                selectChildren(el,val);  
            })
        }
    }
    
    function trimLeafs(node,parent) {
        
            if(!node.items) {
                //da end of the road
                delete parent.items;
            } else {
                node.items.forEach(function(item){
                    trimLeafs(item,node);
                })
            }
        
    }
    
    
   return {
       getAllChildren:getAllChildren,
       getSelected:getSelected,
       selectChildren:selectChildren,
       trimLeafs:trimLeafs,
       treeSearch:treeSearch,
       findParent:findParent
   };
   
})
    
}).call(this);