mirror of
https://github.com/nasa/openmct.git
synced 2025-04-28 23:10:00 +00:00
203 lines
8.5 KiB
JavaScript
203 lines
8.5 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
* Administration. All rights reserved.
|
|
*
|
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* Open MCT includes source code licensed under additional open source
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
* this source code distribution or the Licensing information page available
|
|
* at runtime from the About dialog for additional information.
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Module defining TreeNodeController. Created by vwoeltje on 11/10/14.
|
|
*/
|
|
define(
|
|
[],
|
|
function () {
|
|
|
|
/**
|
|
* 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.)
|
|
*
|
|
* Finally, if a `callback` property is passed in through the
|
|
* `parameters` attribute of the `tree-node`, that callback
|
|
* will be invoked whenever a user clicks in a manner which
|
|
* would result in a selection. This callback is invoked
|
|
* even if the selection does not change (if you are only
|
|
* interested in changes, watch the `selectedObject` property
|
|
* of the object passed in `ng-model` instead.)
|
|
*
|
|
* @memberof platform/commonUI/general
|
|
* @constructor
|
|
*/
|
|
function TreeNodeController($scope, $timeout) {
|
|
var self = this,
|
|
selectedObject = ($scope.ngModel || {}).selectedObject;
|
|
|
|
// 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();
|
|
}
|
|
|
|
// 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) ||
|
|
((navPath[index] === nodePath[index]) &&
|
|
checkPath(nodePath, navPath, index + 1));
|
|
}
|
|
|
|
// Consider the currently-navigated object and update
|
|
// parameters which support display.
|
|
function checkSelection() {
|
|
var nodeObject = $scope.domainObject,
|
|
navObject = selectedObject,
|
|
nodeContext = nodeObject &&
|
|
nodeObject.getCapability('context'),
|
|
navContext = navObject &&
|
|
navObject.getCapability('context'),
|
|
nodePath,
|
|
navPath;
|
|
|
|
// Deselect; we will reselect below, iff we are
|
|
// exactly at the end of the path.
|
|
self.isSelectedFlag = false;
|
|
|
|
// Expand if necessary (if the navigated object will
|
|
// be in this node's subtree)
|
|
if (nodeContext && navContext) {
|
|
// Get the paths as arrays of identifiers
|
|
nodePath = nodeContext.getPath().map(getId);
|
|
navPath = navContext.getPath().map(getId);
|
|
|
|
// Check to see if the node's path lies entirely
|
|
// within the navigation path; otherwise, navigation
|
|
// has happened in some other subtree.
|
|
if (navPath.length >= nodePath.length &&
|
|
checkPath(nodePath, navPath)) {
|
|
|
|
// nodePath is along the navPath; if it's
|
|
// at the end of the path, highlight;
|
|
// otherwise, expand.
|
|
if (nodePath.length === navPath.length) {
|
|
self.isSelectedFlag = true;
|
|
} else { // node path is shorter: Expand!
|
|
if ($scope.toggle) {
|
|
$scope.toggle.setState(true);
|
|
}
|
|
self.trackExpansion();
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Callback for the selection updates; track the currently
|
|
// navigated object and update display parameters as needed.
|
|
function setSelection(object) {
|
|
selectedObject = object;
|
|
checkSelection();
|
|
}
|
|
|
|
this.isSelectedFlag = false;
|
|
this.hasBeenExpandedFlag = false;
|
|
this.$timeout = $timeout;
|
|
this.$scope = $scope;
|
|
|
|
// Listen for changes which will effect display parameters
|
|
$scope.$watch("ngModel.selectedObject", setSelection);
|
|
$scope.$watch("domainObject", checkSelection);
|
|
}
|
|
|
|
/**
|
|
* Select the domain object represented by this node in the tree.
|
|
* This will both update the `selectedObject` property in
|
|
* the object passed in via `ng-model`, and will fire any `callback`
|
|
* passed in via `parameters`.
|
|
*/
|
|
TreeNodeController.prototype.select = function () {
|
|
if (this.$scope.ngModel) {
|
|
this.$scope.ngModel.selectedObject =
|
|
this.$scope.domainObject;
|
|
}
|
|
if ((this.$scope.parameters || {}).callback) {
|
|
this.$scope.parameters.callback(this.$scope.domainObject);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
TreeNodeController.prototype.trackExpansion = function () {
|
|
var self = this;
|
|
if (!self.hasBeenExpanded()) {
|
|
// Run on a timeout; if a lot of expansion needs to
|
|
// occur (e.g. if the selection is several nodes deep) we
|
|
// want this to be spread across multiple digest cycles.
|
|
self.$timeout(function () {
|
|
self.hasBeenExpandedFlag = true;
|
|
}, 0);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Check if this not has ever been expanded.
|
|
* @returns true if it has been expanded
|
|
*/
|
|
TreeNodeController.prototype.hasBeenExpanded = function () {
|
|
return this.hasBeenExpandedFlag;
|
|
};
|
|
|
|
/**
|
|
* Check whether or not the domain object represented by
|
|
* this tree node should be highlighted.
|
|
* An object will be highlighted if it matches
|
|
* ngModel.selectedObject
|
|
* @returns true if this should be highlighted
|
|
*/
|
|
TreeNodeController.prototype.isSelected = function () {
|
|
return this.isSelectedFlag;
|
|
};
|
|
|
|
return TreeNodeController;
|
|
}
|
|
);
|