diff --git a/README.md b/README.md index 441b275acf..f0b0db1670 100644 --- a/README.md +++ b/README.md @@ -122,8 +122,12 @@ correct usage.) * _name_: When used as an object property, this refers to the human-readable name for a thing. (Most often used in the context of extensions, domain object models, or other similar application-specific objects.) +* _navigation_: Refers to the current state of the application with respect + to the user's expressed interest in a specific domain object; e.g. when + a user clicks on a domain object in the tree, they are _navigating_ to + it, and it is thereafter considered the _navigated_ object (until the + user makes another such choice.) * _space_: A name used to identify a persistence store. Interactions with persistence with generally involve a `space` parameter in some form, to distinguish multiple persistence stores from one another (for cases where there are multiple valid persistence locations available.) - \ No newline at end of file diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 2bf8f37c47..f5c0114fb0 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -68,8 +68,8 @@ "templateUrl": "templates/test.html" }, { - "key": "tree-item", - "templateUrl": "templates/tree-item.html", + "key": "tree-node", + "templateUrl": "templates/tree-node.html", "uses": [ "action" ] }, { diff --git a/platform/commonUI/general/res/templates/tree-item.html b/platform/commonUI/general/res/templates/tree-node.html similarity index 100% rename from platform/commonUI/general/res/templates/tree-item.html rename to platform/commonUI/general/res/templates/tree-node.html diff --git a/platform/commonUI/general/res/templates/tree.html b/platform/commonUI/general/res/templates/tree.html index 8a60580c7b..16ffb7d936 100644 --- a/platform/commonUI/general/res/templates/tree.html +++ b/platform/commonUI/general/res/templates/tree.html @@ -1,6 +1,6 @@ diff --git a/platform/commonUI/general/src/TreeNodeController.js b/platform/commonUI/general/src/TreeNodeController.js index 0f4d3a370e..9310b212c1 100644 --- a/platform/commonUI/general/src/TreeNodeController.js +++ b/platform/commonUI/general/src/TreeNodeController.js @@ -9,7 +9,24 @@ define( "use strict"; /** + * The TreeNodeController supports the tree node representation; + * a tree node has a label for the current object as well as a + * subtree which shows (and is not loaded until) the node is + * expanded. * + * This controller tracks the following, so that the tree node + * template may update its state accordingly: + * + * * Whether or not the tree node has ever been expanded (this + * is used to lazily load, exactly once, the subtree) + * * Whether or not the node is currently the domain object + * of navigation (this gets highlighted differently to + * provide the user with visual feedback.) + * + * Additionally, this controller will automatically trigger + * node expansion when this tree node's _subtree_ will contain + * the navigated object (recursively, this becomes an + * expand-to-show-navigated-object behavior.) * @constructor */ function TreeNodeController($scope, navigationService) { @@ -17,30 +34,45 @@ define( isNavigated = false, hasBeenExpanded = false; - function idsEqual(objA, objB) { - return (objA === objB) || - (objA && objB && (objA.getId() === objB.getId())); + // Look up the id for a domain object. A convenience + // for mapping; additionally does some undefined-checking. + function getId(obj) { + return obj && obj.getId && obj.getId(); } + // Check if two domain objects have the same ID + function idsEqual(objA, objB) { + return getId(objA) === getId(objB); + } + + // Get the parent of a domain object, as reported by + // its context capability. This is used to distinguish + // two different instances of a domain object that have + // been reached in different ways. function parentOf(domainObject) { var context = domainObject && domainObject.getCapability("context"); return context && context.getParent(); } - function getId(obj) { - return obj.getId(); - } - // Verify that id paths are equivalent, staring at // index, ending at the end of the node path. function checkPath(nodePath, navPath, index) { index = index || 0; + + // The paths overlap if we have made it past the + // end of the node's path; otherwise, check the + // id at the current index for equality and perform + // a recursive step for subsequent ids in the paths, + // until we exceed path length or hit a mismatch. return (index >= nodePath.length) || (idsEqual(navPath[index], nodePath[index]) && checkPath(nodePath, navPath, index + 1)); } + // Check if the navigated object is in the subtree of this + // node's domain object, by comparing the paths reported + // by their context capability. function isOnNavigationPath(nodeObject, navObject) { var nodeContext = nodeObject && nodeObject.getCapability('context'), @@ -58,14 +90,20 @@ define( return false; // No context to judge by } + // Consider the currently-navigated object and update + // parameters which support display. function checkNavigation() { var nodeObject = $scope.domainObject; + // Check if we are the navigated object. Check the parent + // as well to make sure we are the same instance of the + // navigated object. isNavigated = idsEqual(nodeObject, navigatedObject) && idsEqual(parentOf(nodeObject), parentOf(navigatedObject)); - // Expand if necessary + // Expand if necessary (if the navigated object will + // be in this node's subtree) if (isOnNavigationPath(nodeObject, navigatedObject) && $scope.toggle !== undefined) { $scope.toggle.setState(true); @@ -73,11 +111,14 @@ define( } } + // Callback for the navigation service; track the currently + // navigated object and update display parameters as needed. function setNavigation(object) { navigatedObject = object; checkNavigation(); } + // Listen for changes which will effect display parameters navigationService.addListener(setNavigation); $scope.$on("$destroy", function () { navigationService.removeListener(setNavigation); @@ -85,12 +126,26 @@ define( $scope.$watch("domainObject", checkNavigation); return { + /** + * This method should be called when a node is expanded + * to record that this has occurred, to support one-time + * lazy loading of the node's subtree. + */ trackExpansion: function () { hasBeenExpanded = true; }, + /** + * Check if this not has ever been expanded. + * @returns true if it has been expanded + */ hasBeenExpanded: function () { return hasBeenExpanded; }, + /** + * Check whether or not the domain object represented by + * this tree node is currently the navigated object. + * @returns true if this is the navigated object + */ isNavigated: function () { return isNavigated; }