From fe4094df64398faef82823447dad20a6d1ea6c4a Mon Sep 17 00:00:00 2001 From: Ehsan Khakifirooz Date: Mon, 18 Jul 2016 14:24:04 +0100 Subject: [PATCH 1/3] A rocover fix for the deep circular object 'Maximum call stack size exceeded' error in defaultEquality function --- angular-tree-control.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/angular-tree-control.js b/angular-tree-control.js index 3043027..f2611b0 100644 --- a/angular-tree-control.js +++ b/angular-tree-control.js @@ -48,6 +48,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex return dst || src; } + function defaultEquality(a, b,$scope) { if (!a || !b) return false; @@ -55,9 +56,16 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex a[$scope.options.nodeChildren] = []; b = shallowCopy(b); b[$scope.options.nodeChildren] = []; - return angular.equals(a, b); + + var isEqual = false; + try{ + isEqual = angular.equals(a, b); + }catch(ex){ + console.warn(ex.message); + } + return isEqual; } - + function defaultIsSelectable() { return true; } From cb2d33c9116dca49227fb04427f99a6bbacb8529 Mon Sep 17 00:00:00 2001 From: Ehsan Khakifirooz Date: Tue, 26 Jul 2016 12:36:48 +0100 Subject: [PATCH 2/3] Underscore is used as the module for objects comparison --- angular-tree-control.js | 169 ++++++++++++++++++++++------------------ bower.json | 3 +- 2 files changed, 95 insertions(+), 77 deletions(-) diff --git a/angular-tree-control.js b/angular-tree-control.js index f2611b0..09fdaf7 100644 --- a/angular-tree-control.js +++ b/angular-tree-control.js @@ -1,8 +1,8 @@ /* commonjs package manager support (eg componentjs) */ -if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ - module.exports = 'treeControl'; +if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports) { + module.exports = 'treeControl'; } -(function ( angular ) { +(function (window, angular) { 'use strict'; function createPath(startScope) { @@ -49,23 +49,23 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex return dst || src; } - function defaultEquality(a, b,$scope) { + function defaultEquality(a, b, $scope) { if (!a || !b) return false; a = shallowCopy(a); a[$scope.options.nodeChildren] = []; b = shallowCopy(b); b[$scope.options.nodeChildren] = []; - + var isEqual = false; - try{ - isEqual = angular.equals(a, b); - }catch(ex){ + try { + isEqual = window._.isEqual(a, b); + } catch (ex) { console.warn(ex.message); } return isEqual; } - + function defaultIsSelectable() { return true; } @@ -88,12 +88,12 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex ensureDefault($scope.options, "allowDeselect", true); ensureDefault($scope.options, "isSelectable", defaultIsSelectable); } - - angular.module( 'treeControl', [] ) + + angular.module('treeControl', []) .constant('treeConfig', { templateUrl: null }) - .directive( 'treecontrol', ['$compile', function( $compile ) { + .directive('treecontrol', ['$compile', function ($compile) { /** * @param cssClass - the css class * @param addClassProperty - should we wrap the class name with class="" @@ -108,9 +108,8 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex else return ""; } - - - + + return { restrict: 'EA', require: "treecontrol", @@ -129,26 +128,26 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex filterComparator: "=?" }, controller: ['$scope', '$templateCache', '$interpolate', 'treeConfig', function ($scope, $templateCache, $interpolate, treeConfig) { - + $scope.options = $scope.options || {}; - + ensureAllDefaultOptions($scope); - + $scope.selectedNodes = $scope.selectedNodes || []; $scope.expandedNodes = $scope.expandedNodes || []; $scope.expandedNodesMap = {}; - for (var i=0; i < $scope.expandedNodes.length; i++) { - $scope.expandedNodesMap["a"+i] = $scope.expandedNodes[i]; + for (var i = 0; i < $scope.expandedNodes.length; i++) { + $scope.expandedNodesMap["a" + i] = $scope.expandedNodes[i]; } $scope.parentScopeOfTree = $scope.$parent; function isSelectedNode(node) { - if (!$scope.options.multiSelection && ($scope.options.equality(node, $scope.selectedNode , $scope))) + if (!$scope.options.multiSelection && ($scope.options.equality(node, $scope.selectedNode, $scope))) return true; else if ($scope.options.multiSelection && $scope.selectedNodes) { for (var i = 0; (i < $scope.selectedNodes.length); i++) { - if ($scope.options.equality(node, $scope.selectedNodes[i] , $scope)) { + if ($scope.options.equality(node, $scope.selectedNodes[i], $scope)) { return true; } } @@ -156,7 +155,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex } } - $scope.headClass = function(node) { + $scope.headClass = function (node) { var liSelectionClass = classIfDefined($scope.options.injectClasses.liSelected, false); var injectSelectionClass = ""; if (liSelectionClass && isSelectedNode(node)) @@ -169,18 +168,18 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex return "tree-collapsed" + injectSelectionClass; }; - $scope.iBranchClass = function() { + $scope.iBranchClass = function () { if ($scope.expandedNodesMap[this.$id]) return classIfDefined($scope.options.injectClasses.iExpanded); else return classIfDefined($scope.options.injectClasses.iCollapsed); }; - $scope.nodeExpanded = function() { + $scope.nodeExpanded = function () { return !!$scope.expandedNodesMap[this.$id]; }; - $scope.selectNodeHead = function() { + $scope.selectNodeHead = function () { var transcludedScope = this; var expanding = $scope.expandedNodesMap[transcludedScope.$id] === undefined; $scope.expandedNodesMap[transcludedScope.$id] = (expanding ? transcludedScope.node : undefined); @@ -189,8 +188,8 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex } else { var index; - for (var i=0; (i < $scope.expandedNodes.length) && !index; i++) { - if ($scope.options.equality($scope.expandedNodes[i], transcludedScope.node , $scope)) { + for (var i = 0; (i < $scope.expandedNodes.length) && !index; i++) { + if ($scope.options.equality($scope.expandedNodes[i], transcludedScope.node, $scope)) { index = i; } } @@ -198,22 +197,31 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex $scope.expandedNodes.splice(index, 1); } if ($scope.onNodeToggle) { - var parentNode = (transcludedScope.$parent.node === transcludedScope.synteticRoot)?null:transcludedScope.$parent.node; + var parentNode = (transcludedScope.$parent.node === transcludedScope.synteticRoot) ? null : transcludedScope.$parent.node; var path = createPath(transcludedScope); - $scope.onNodeToggle({node: transcludedScope.node, $parentNode: parentNode, $path: path, - $index: transcludedScope.$index, $first: transcludedScope.$first, $middle: transcludedScope.$middle, - $last: transcludedScope.$last, $odd: transcludedScope.$odd, $even: transcludedScope.$even, expanded: expanding}); + $scope.onNodeToggle({ + node: transcludedScope.node, + $parentNode: parentNode, + $path: path, + $index: transcludedScope.$index, + $first: transcludedScope.$first, + $middle: transcludedScope.$middle, + $last: transcludedScope.$last, + $odd: transcludedScope.$odd, + $even: transcludedScope.$even, + expanded: expanding + }); } }; - $scope.selectNodeLabel = function( selectedNode){ + $scope.selectNodeLabel = function (selectedNode) { var transcludedScope = this; - if(!$scope.options.isLeaf(selectedNode, $scope) && (!$scope.options.dirSelectable || !$scope.options.isSelectable(selectedNode))) { + if (!$scope.options.isLeaf(selectedNode, $scope) && (!$scope.options.dirSelectable || !$scope.options.isSelectable(selectedNode))) { // Branch node is not selectable, expand this.selectNodeHead(); } - else if($scope.options.isLeaf(selectedNode, $scope) && (!$scope.options.isSelectable(selectedNode))) { + else if ($scope.options.isLeaf(selectedNode, $scope) && (!$scope.options.isSelectable(selectedNode))) { // Leaf node is not selectable return; } @@ -221,8 +229,8 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex var selected = false; if ($scope.options.multiSelection) { var pos = -1; - for (var i=0; i < $scope.selectedNodes.length; i++) { - if($scope.options.equality(selectedNode, $scope.selectedNodes[i] , $scope)) { + for (var i = 0; i < $scope.selectedNodes.length; i++) { + if ($scope.options.equality(selectedNode, $scope.selectedNodes[i], $scope)) { pos = i; break; } @@ -234,7 +242,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex $scope.selectedNodes.splice(pos, 1); } } else { - if (!$scope.options.equality(selectedNode, $scope.selectedNode , $scope)) { + if (!$scope.options.equality(selectedNode, $scope.selectedNode, $scope)) { $scope.selectedNode = selectedNode; selected = true; } @@ -248,16 +256,25 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex } } if ($scope.onSelection) { - var parentNode = (transcludedScope.$parent.node === transcludedScope.synteticRoot)?null:transcludedScope.$parent.node; + var parentNode = (transcludedScope.$parent.node === transcludedScope.synteticRoot) ? null : transcludedScope.$parent.node; var path = createPath(transcludedScope) - $scope.onSelection({node: selectedNode, selected: selected, $parentNode: parentNode, $path: path, - $index: transcludedScope.$index, $first: transcludedScope.$first, $middle: transcludedScope.$middle, - $last: transcludedScope.$last, $odd: transcludedScope.$odd, $even: transcludedScope.$even}); + $scope.onSelection({ + node: selectedNode, + selected: selected, + $parentNode: parentNode, + $path: path, + $index: transcludedScope.$index, + $first: transcludedScope.$first, + $middle: transcludedScope.$middle, + $last: transcludedScope.$last, + $odd: transcludedScope.$odd, + $even: transcludedScope.$even + }); } } }; - $scope.selectedClass = function() { + $scope.selectedClass = function () { var isThisNodeSelected = isSelectedNode(this.node); var labelSelectionClass = classIfDefined($scope.options.injectClasses.labelSelected, false); var injectSelectionClass = ""; @@ -267,26 +284,26 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex return isThisNodeSelected ? "tree-selected" + injectSelectionClass : ""; }; - $scope.unselectableClass = function() { + $scope.unselectableClass = function () { var isThisNodeUnselectable = !$scope.options.isSelectable(this.node); var labelUnselectableClass = classIfDefined($scope.options.injectClasses.labelUnselectable, false); return isThisNodeUnselectable ? "tree-unselectable " + labelUnselectableClass : ""; }; //tree template - $scope.isReverse = function() { - return !($scope.reverseOrder === 'false' || $scope.reverseOrder === 'False' || $scope.reverseOrder === '' || $scope.reverseOrder === false); + $scope.isReverse = function () { + return !($scope.reverseOrder === 'false' || $scope.reverseOrder === 'False' || $scope.reverseOrder === '' || $scope.reverseOrder === false); }; - $scope.orderByFunc = function() { - return $scope.orderBy; + $scope.orderByFunc = function () { + return $scope.orderBy; }; // return "" + $scope.orderBy; var templateOptions = { orderBy: $scope.orderBy ? " | orderBy:orderByFunc():isReverse()" : '', ulClass: classIfDefined($scope.options.injectClasses.ul, true), - nodeChildren: $scope.options.nodeChildren, + nodeChildren: $scope.options.nodeChildren, liClass: classIfDefined($scope.options.injectClasses.li, true), iLeafClass: classIfDefined($scope.options.injectClasses.iLeaf, false), labelClass: classIfDefined($scope.options.injectClasses.label, false) @@ -295,11 +312,11 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex var template; var templateUrl = $scope.options.templateUrl || treeConfig.templateUrl; - if(templateUrl) { + if (templateUrl) { template = $templateCache.get(templateUrl); } - if(!template) { + if (!template) { template = '