/* commonjs package manager support (eg componentjs) */ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ module.exports = 'treeControl'; } (function ( angular ) { 'use strict'; function createPath(startScope) { return function path() { var _path = []; var scope = startScope; var prevNode; while (scope && scope.node !== startScope.synteticRoot) { if (prevNode !== scope.node) _path.push(scope.node); prevNode = scope.node; scope = scope.$parent; } 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', { templateUrl: null }) .directive( 'treecontrol', ['$compile', function( $compile ) { /** * @param cssClass - the css class * @param addClassProperty - should we wrap the class name with class="" */ function classIfDefined(cssClass, addClassProperty) { if (cssClass) { if (addClassProperty) return 'class="' + cssClass + '"'; else return cssClass; } else return ""; } return { restrict: 'EA', require: "treecontrol", transclude: true, scope: { treeModel: "=", selectedNode: "=?", selectedNodes: "=?", expandedNodes: "=?", onSelection: "&", onNodeToggle: "&", options: "=?", orderBy: "=?", reverseOrder: "@", filterExpression: "=?", 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]; } $scope.parentScopeOfTree = $scope.$parent; function isSelectedNode(node) { 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)) { return true; } } return false; } } $scope.headClass = function(node) { var liSelectionClass = classIfDefined($scope.options.injectClasses.liSelected, false); var injectSelectionClass = ""; if (liSelectionClass && isSelectedNode(node)) injectSelectionClass = " " + liSelectionClass; if ($scope.options.isLeaf(node, $scope)) return "tree-leaf" + injectSelectionClass; if ($scope.expandedNodesMap[this.$id]) return "tree-expanded" + injectSelectionClass; else return "tree-collapsed" + injectSelectionClass; }; $scope.iBranchClass = function() { if ($scope.expandedNodesMap[this.$id]) return classIfDefined($scope.options.injectClasses.iExpanded); else return classIfDefined($scope.options.injectClasses.iCollapsed); }; $scope.nodeExpanded = function() { return !!$scope.expandedNodesMap[this.$id]; }; $scope.selectNodeHead = function() { var transcludedScope = this; var expanding = $scope.expandedNodesMap[transcludedScope.$id] === undefined; $scope.expandedNodesMap[transcludedScope.$id] = (expanding ? transcludedScope.node : undefined); if (expanding) { $scope.expandedNodes.push(transcludedScope.node); } else { var index; for (var i=0; (i < $scope.expandedNodes.length) && !index; i++) { if ($scope.options.equality($scope.expandedNodes[i], transcludedScope.node , $scope)) { index = i; } } if (index !== undefined) $scope.expandedNodes.splice(index, 1); } if ($scope.onNodeToggle) { 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.selectNodeLabel = function( selectedNode){ var transcludedScope = this; 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))) { // Leaf node is not selectable return; } else { 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)) { pos = i; break; } } if (pos === -1) { $scope.selectedNodes.push(selectedNode); selected = true; } else { $scope.selectedNodes.splice(pos, 1); } } else { if (!$scope.options.equality(selectedNode, $scope.selectedNode , $scope)) { $scope.selectedNode = selectedNode; selected = true; } else { if ($scope.options.allowDeselect) { $scope.selectedNode = undefined; } else { $scope.selectedNode = selectedNode; selected = true; } } } if ($scope.onSelection) { 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.selectedClass = function() { var isThisNodeSelected = isSelectedNode(this.node); var labelSelectionClass = classIfDefined($scope.options.injectClasses.labelSelected, false); var injectSelectionClass = ""; if (labelSelectionClass && isThisNodeSelected) injectSelectionClass = " " + labelSelectionClass; return isThisNodeSelected ? "tree-selected" + injectSelectionClass : ""; }; $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.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, liClass: classIfDefined($scope.options.injectClasses.li, true), iLeafClass: classIfDefined($scope.options.injectClasses.iLeaf, false), labelClass: classIfDefined($scope.options.injectClasses.label, false) }; var template; var templateUrl = $scope.options.templateUrl || treeConfig.templateUrl; if(templateUrl) { template = $templateCache.get(templateUrl); } if(!template) { template = '