diff --git a/.gitignore b/.gitignore
index 94783d5..5e22086 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ bower_components
coverage
treecontrol.iml
.DS_Store
+Chromium 49.0.2623 (Ubuntu)
diff --git a/Gruntfile.js b/Gruntfile.js
index 1d3351c..4851758 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -30,7 +30,7 @@ module.exports = function(grunt) {
background: true,
options: {
files: [
- 'bower_components/jquery/jquery.js',
+ 'bower_components/jquery/dist/jquery.js',
'demo/angular.1.3.12.js',
'demo/angular-mocks.1.3.12.js',
'angular-tree-control.js',
diff --git a/angular-tree-control.js b/angular-tree-control.js
index 3c492a8..418a916 100644
--- a/angular-tree-control.js
+++ b/angular-tree-control.js
@@ -19,6 +19,67 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
return _path;
}
}
+
+ function ensureDefault(obj, prop, value) {
+ if (!obj.hasOwnProperty(prop))
+ obj[prop] = value;
+ }
+
+ function defaultIsLeaf(node, $scope) {
+ return !node[$scope.options.nodeChildren] || node[$scope.options.nodeChildren].length === 0;
+ }
+
+ function shallowCopy(src, dst) {
+ if (angular.isArray(src)) {
+ dst = dst || [];
+
+ for (var i = 0; i < src.length; i++) {
+ dst[i] = src[i];
+ }
+ } else if (angular.isObject(src)) {
+ dst = dst || {};
+
+ for (var key in src) {
+ if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
+ dst[key] = src[key];
+ }
+ }
+ }
+
+ return dst || src;
+ }
+ function defaultEquality(a, b,$scope) {
+ if (!a || !b)
+ return false;
+ a = shallowCopy(a);
+ a[$scope.options.nodeChildren] = [];
+ b = shallowCopy(b);
+ b[$scope.options.nodeChildren] = [];
+ return angular.equals(a, b);
+ }
+
+ function defaultIsSelectable() {
+ return true;
+ }
+
+ function ensureAllDefaultOptions($scope) {
+ ensureDefault($scope.options, "multiSelection", false);
+ ensureDefault($scope.options, "nodeChildren", "children");
+ ensureDefault($scope.options, "dirSelectable", "true");
+ ensureDefault($scope.options, "injectClasses", {});
+ ensureDefault($scope.options.injectClasses, "ul", "");
+ ensureDefault($scope.options.injectClasses, "li", "");
+ ensureDefault($scope.options.injectClasses, "liSelected", "");
+ ensureDefault($scope.options.injectClasses, "iExpanded", "");
+ ensureDefault($scope.options.injectClasses, "iCollapsed", "");
+ ensureDefault($scope.options.injectClasses, "iLeaf", "");
+ ensureDefault($scope.options.injectClasses, "label", "");
+ ensureDefault($scope.options.injectClasses, "labelSelected", "");
+ ensureDefault($scope.options, "equality", defaultEquality);
+ ensureDefault($scope.options, "isLeaf", defaultIsLeaf);
+ ensureDefault($scope.options, "allowDeselect", true);
+ ensureDefault($scope.options, "isSelectable", defaultIsSelectable);
+ }
angular.module( 'treeControl', [] )
.constant('treeConfig', {
@@ -40,10 +101,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
return "";
}
- function ensureDefault(obj, prop, value) {
- if (!obj.hasOwnProperty(prop))
- obj[prop] = value;
- }
+
return {
restrict: 'EA',
@@ -62,62 +120,11 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
filterExpression: "=?",
filterComparator: "=?"
},
- controller: ['$scope', '$templateCache', '$interpolate', 'treeConfig', function( $scope, $templateCache, $interpolate, treeConfig ) {
-
- function defaultIsLeaf(node) {
- return !node[$scope.options.nodeChildren] || node[$scope.options.nodeChildren].length === 0;
- }
-
- function shallowCopy(src, dst) {
- if (angular.isArray(src)) {
- dst = dst || [];
-
- for ( var i = 0; i < src.length; i++) {
- dst[i] = src[i];
- }
- } else if (angular.isObject(src)) {
- dst = dst || {};
-
- for (var key in src) {
- if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
- dst[key] = src[key];
- }
- }
- }
-
- return dst || src;
- }
- function defaultEquality(a, b) {
- if (!a || !b )
- return false;
- a = shallowCopy(a);
- a[$scope.options.nodeChildren] = [];
- b = shallowCopy(b);
- b[$scope.options.nodeChildren] = [];
- return angular.equals(a, b);
- }
-
- function defaultIsSelectable() {
- return true;
- }
-
+ controller: ['$scope', '$templateCache', '$interpolate', 'treeConfig', function ($scope, $templateCache, $interpolate, treeConfig) {
+
$scope.options = $scope.options || {};
- ensureDefault($scope.options, "multiSelection", false);
- ensureDefault($scope.options, "nodeChildren", "children");
- ensureDefault($scope.options, "dirSelectable", "true");
- ensureDefault($scope.options, "injectClasses", {});
- ensureDefault($scope.options.injectClasses, "ul", "");
- ensureDefault($scope.options.injectClasses, "li", "");
- ensureDefault($scope.options.injectClasses, "liSelected", "");
- ensureDefault($scope.options.injectClasses, "iExpanded", "");
- ensureDefault($scope.options.injectClasses, "iCollapsed", "");
- ensureDefault($scope.options.injectClasses, "iLeaf", "");
- ensureDefault($scope.options.injectClasses, "label", "");
- ensureDefault($scope.options.injectClasses, "labelSelected", "");
- ensureDefault($scope.options, "equality", defaultEquality);
- ensureDefault($scope.options, "isLeaf", defaultIsLeaf);
- ensureDefault($scope.options, "allowDeselect", true);
- ensureDefault($scope.options, "isSelectable", defaultIsSelectable);
+
+ ensureAllDefaultOptions($scope);
$scope.selectedNodes = $scope.selectedNodes || [];
$scope.expandedNodes = $scope.expandedNodes || [];
@@ -129,11 +136,11 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
function isSelectedNode(node) {
- if (!$scope.options.multiSelection && ($scope.options.equality(node, $scope.selectedNode)))
+ 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])) {
+ if ($scope.options.equality(node, $scope.selectedNodes[i] , $scope)) {
return true;
}
}
@@ -146,7 +153,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
var injectSelectionClass = "";
if (liSelectionClass && isSelectedNode(node))
injectSelectionClass = " " + liSelectionClass;
- if ($scope.options.isLeaf(node))
+ if ($scope.options.isLeaf(node, $scope))
return "tree-leaf" + injectSelectionClass;
if ($scope.expandedNodesMap[this.$id])
return "tree-expanded" + injectSelectionClass;
@@ -175,7 +182,7 @@ 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)) {
+ if ($scope.options.equality($scope.expandedNodes[i], transcludedScope.node , $scope)) {
index = i;
}
}
@@ -194,11 +201,11 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
$scope.selectNodeLabel = function( selectedNode){
var transcludedScope = this;
- if(!$scope.options.isLeaf(selectedNode) && (!$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.options.isSelectable(selectedNode))) {
+ else if($scope.options.isLeaf(selectedNode, $scope) && (!$scope.options.isSelectable(selectedNode))) {
// Leaf node is not selectable
return;
}
@@ -207,7 +214,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
if ($scope.options.multiSelection) {
var pos = -1;
for (var i=0; i < $scope.selectedNodes.length; i++) {
- if($scope.options.equality(selectedNode, $scope.selectedNodes[i])) {
+ if($scope.options.equality(selectedNode, $scope.selectedNodes[i] , $scope)) {
pos = i;
break;
}
@@ -219,7 +226,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
$scope.selectedNodes.splice(pos, 1);
}
} else {
- if (!$scope.options.equality(selectedNode, $scope.selectedNode)) {
+ if (!$scope.options.equality(selectedNode, $scope.selectedNode , $scope)) {
$scope.selectedNode = selectedNode;
selected = true;
}
@@ -338,7 +345,7 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
var found = false;
for (var i=0; (i < existingScopes.length) && !found; i++) {
var existingScope = existingScopes[i];
- if (scope.options.equality(newExNode, existingScope.node)) {
+ if (scope.options.equality(newExNode, existingScope.node , scope)) {
newExpandedNodesMap[existingScope.$id] = existingScope.node;
found = true;
}
@@ -386,23 +393,27 @@ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.ex
}
};
})
- .directive("treeTransclude", function() {
+ .directive("treeTransclude", function () {
return {
+ controller: function ($scope) {
+ ensureAllDefaultOptions($scope);
+ },
+
link: function(scope, element, attrs, controller) {
- if (!scope.options.isLeaf(scope.node)) {
+ if (!scope.options.isLeaf(scope.node, scope)) {
angular.forEach(scope.expandedNodesMap, function (node, id) {
- if (scope.options.equality(node, scope.node)) {
+ if (scope.options.equality(node, scope.node , scope)) {
scope.expandedNodesMap[scope.$id] = scope.node;
scope.expandedNodesMap[id] = undefined;
}
});
}
- if (!scope.options.multiSelection && scope.options.equality(scope.node, scope.selectedNode)) {
+ if (!scope.options.multiSelection && scope.options.equality(scope.node, scope.selectedNode , scope)) {
scope.selectedNode = scope.node;
} else if (scope.options.multiSelection) {
var newSelectedNodes = [];
for (var i = 0; (i < scope.selectedNodes.length); i++) {
- if (scope.options.equality(scope.node, scope.selectedNodes[i])) {
+ if (scope.options.equality(scope.node, scope.selectedNodes[i] , scope)) {
newSelectedNodes.push(scope.node);
}
}
diff --git a/karma.conf.js b/karma.conf.js
index 2fbb4ce..936c672 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -12,7 +12,7 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
- 'bower_components/jquery/jquery.js',
+ 'bower_components/jquery/dist/jquery.js',
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'angular-tree-control.js',
diff --git a/test/angular-tree-control-test.js b/test/angular-tree-control-test.js
index 5827415..ceddf5f 100644
--- a/test/angular-tree-control-test.js
+++ b/test/angular-tree-control-test.js
@@ -98,6 +98,28 @@ describe('treeControl', function() {
expect(element.find('li.tree-leaf').length).toBe(3);
});
+ it('should display first level parents as leafs, based on condition', function () {
+ $rootScope.treedata = createSubTree(2, 2);
+ // reverse which is leaf and which is branch - now we have 2 leafs that are not expanded
+ $rootScope.treeOptions = {isLeaf: function(node) {return node.children.length > 0;}};
+ element = $compile('{{node.label}}')($rootScope);
+ $rootScope.$digest();
+ expect(element.find('li.tree-collapsed').length).toBe(0);
+ expect(element.find('li.tree-leaf').length).toBe(2);
+ });
+
+ it('should display second level as branches, based on condition', function () {
+ $rootScope.treedata = createSubTree(2, 2);
+ // reverse which is leaf and which is branch - now we have 2 leafs that are not expanded
+ $rootScope.treeOptions = {isLeaf: function(node) {return node.children.length > 0;}};
+ element = $compile('{{node.label}}')($rootScope);
+ $rootScope.$digest();
+ element.find('li:eq(1) .tree-branch-head').click();
+ element.find('li:eq(0) .tree-branch-head').click();
+ // now the first level "leafs" are expanded, and we have 4 second level "branches"
+ expect(element.find('li.tree-collapsed').length).toBe(4);
+ expect(element.find('li.tree-leaf').length).toBe(2);
+ });
});
describe('rendering using external scope data', function () {