Compare commits
	
		
			46 Commits
		
	
	
		
			style-guid
			...
			open315
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ea21cd7ccf | ||
| 
						 | 
					d1f74677a2 | ||
| 
						 | 
					8a783d4a87 | ||
| 
						 | 
					342a6b4579 | ||
| 
						 | 
					7fb45888d4 | ||
| 
						 | 
					173be63844 | ||
| 
						 | 
					9df54e875f | ||
| 
						 | 
					c500d0b84e | ||
| 
						 | 
					1c0be97a6d | ||
| 
						 | 
					0d06fe3c07 | ||
| 
						 | 
					0946e82dd1 | ||
| 
						 | 
					c7b094f26e | ||
| 
						 | 
					e898fee504 | ||
| 
						 | 
					c02d1ae902 | ||
| 
						 | 
					3f80951ba9 | ||
| 
						 | 
					deb4314dce | ||
| 
						 | 
					b0812bf8e3 | ||
| 
						 | 
					4fea6b932c | ||
| 
						 | 
					6a7727e1c6 | ||
| 
						 | 
					de136bc19a | ||
| 
						 | 
					404327e583 | ||
| 
						 | 
					e12e0d72da | ||
| 
						 | 
					4eb17342f6 | ||
| 
						 | 
					e453868994 | ||
| 
						 | 
					111ebe83da | ||
| 
						 | 
					a16cb16c31 | ||
| 
						 | 
					b5fb2176e9 | ||
| 
						 | 
					70e11d66e1 | ||
| 
						 | 
					a039b9b5fe | ||
| 
						 | 
					5fdffee9a5 | ||
| 
						 | 
					1781e9be32 | ||
| 
						 | 
					756e445a80 | ||
| 
						 | 
					b450d36472 | ||
| 
						 | 
					b5d2949b8f | ||
| 
						 | 
					c6eb07a810 | ||
| 
						 | 
					63ce7349e3 | ||
| 
						 | 
					add4e22cd3 | ||
| 
						 | 
					40bd04f455 | ||
| 
						 | 
					4fee1ee153 | ||
| 
						 | 
					cfb6d4ccbf | ||
| 
						 | 
					50db3287db | ||
| 
						 | 
					4a3ecf1435 | ||
| 
						 | 
					c24c3d4534 | ||
| 
						 | 
					d6d57a396a | ||
| 
						 | 
					16781b6156 | ||
| 
						 | 
					24e1c1ff8c | 
@@ -1113,9 +1113,9 @@ contents of this object are managed entirely by the view/representation which
 | 
			
		||||
receives it. 
 | 
			
		||||
* `representation`: An empty object, useful as a 'scratch pad' for 
 | 
			
		||||
representation state. 
 | 
			
		||||
* `ngModel`: An object passed through the ng-model attribute of the 
 | 
			
		||||
* `mctModel`: An object passed through the `mct-model` attribute of the
 | 
			
		||||
`mct-representation` , if any. 
 | 
			
		||||
* `parameters`: An object passed through the parameters attribute of the 
 | 
			
		||||
* `parameters`: An object passed through the `parameters` attribute of the
 | 
			
		||||
`mct-representation`, if any. 
 | 
			
		||||
* Any capabilities requested by the uses property of the representation 
 | 
			
		||||
definition.
 | 
			
		||||
@@ -1507,10 +1507,12 @@ attributes, all of which are specified as Angular expressions:
 | 
			
		||||
 | 
			
		||||
* `key`: Machine-readable identifier for the template (of extension category  
 | 
			
		||||
templates ) to be displayed. 
 | 
			
		||||
* `ng-model`: _Optional_; will be passed into the template's scope as `ngModel`. 
 | 
			
		||||
Intended usage is for two-way bound user input.
 | 
			
		||||
* `mct-model`: _Optional_; will be passed into the template's scope as `mctModel`.
 | 
			
		||||
Intended usage is for modification by the template.
 | 
			
		||||
Note that this value will _not_ be two-way bound, so bi-directional
 | 
			
		||||
communication should be achieved by modifying _properties_ on the object.
 | 
			
		||||
* `parameters`: _Optional_; will be passed into the template's scope as 
 | 
			
		||||
parameters. Intended usage is for template-specific display parameters. 
 | 
			
		||||
`parameters`. Intended usage is for template-specific display parameters.
 | 
			
		||||
 | 
			
		||||
## Representation 
 | 
			
		||||
 | 
			
		||||
@@ -1522,11 +1524,14 @@ attributes, all of which are specified as Angular expressions:
 | 
			
		||||
 | 
			
		||||
* `key`: Machine-readable identifier for the representation (of extension 
 | 
			
		||||
category _representations_ or _views_ ) to be displayed. 
 | 
			
		||||
* `mct-object`: The domain object being represented. 
 | 
			
		||||
* `ng-model`: Optional; will be passed into the template's scope as `ngModel`. 
 | 
			
		||||
Intended usage is for two-way bound user input. 
 | 
			
		||||
* `parameters`: Optional; will be passed into the template's scope as  
 | 
			
		||||
parameters . Intended usage is for template-specific display parameters. 
 | 
			
		||||
* `mct-object`: The domain object being represented. Will be available in the
 | 
			
		||||
representation's scope as `domainObject`.
 | 
			
		||||
* `mct-model`: Optional; will be passed into the template's scope as `mctModel`.
 | 
			
		||||
Intended usage is for modification by the template.
 | 
			
		||||
Note that this value will _not_ be two-way bound, so bi-directional
 | 
			
		||||
communication should be achieved by modifying _properties_ on the object.
 | 
			
		||||
* `parameters`: Optional; will be passed into the representation's scope as
 | 
			
		||||
`parameters`. Intended usage is for representation-specific display parameters.
 | 
			
		||||
 | 
			
		||||
## Resize 
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,7 @@ define([
 | 
			
		||||
    "./src/directives/MCTContainer",
 | 
			
		||||
    "./src/directives/MCTDrag",
 | 
			
		||||
    "./src/directives/MCTClickElsewhere",
 | 
			
		||||
    "./src/directives/MCTRefresh",
 | 
			
		||||
    "./src/directives/MCTResize",
 | 
			
		||||
    "./src/directives/MCTPopup",
 | 
			
		||||
    "./src/directives/MCTScroll",
 | 
			
		||||
@@ -92,6 +93,7 @@ define([
 | 
			
		||||
    MCTContainer,
 | 
			
		||||
    MCTDrag,
 | 
			
		||||
    MCTClickElsewhere,
 | 
			
		||||
    MCTRefresh,
 | 
			
		||||
    MCTResize,
 | 
			
		||||
    MCTPopup,
 | 
			
		||||
    MCTScroll,
 | 
			
		||||
@@ -344,6 +346,10 @@ define([
 | 
			
		||||
                        "$document"
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "mctRefresh",
 | 
			
		||||
                    "implementation": MCTRefresh
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "mctResize",
 | 
			
		||||
                    "implementation": MCTResize,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,10 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="t-object-label l-flex-row flex-elem grows">
 | 
			
		||||
    <div class="t-item-icon flex-elem" ng-class="{ 'l-icon-link':location.isLink() }">
 | 
			
		||||
        <div class="t-item-icon-glyph">{{type.getGlyph()}}</div>
 | 
			
		||||
<div class="t-object-label l-flex-row flex-elem grows"
 | 
			
		||||
     mct-refresh="domainObject.getCapability('mutation').listen(callback)">
 | 
			
		||||
    <div class="t-item-icon flex-elem" ng-class="::{ 'l-icon-link':location.isLink() }">
 | 
			
		||||
        <div class="t-item-icon-glyph">{{::type.getGlyph()}}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class='t-title-label flex-elem grows'>{{model.name}}</div>
 | 
			
		||||
    <div class='t-title-label flex-elem grows'>{{::model.name}}</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,18 +19,19 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<ul class="tree">
 | 
			
		||||
    <li ng-if="!composition">
 | 
			
		||||
<ul class="tree"
 | 
			
		||||
    mct-refresh="domainObject.getCapability('mutation').listen(callback)">
 | 
			
		||||
    <li ng-hide="::composition">
 | 
			
		||||
        <span class="tree-item">
 | 
			
		||||
            <span class="icon wait-spinner"></span>
 | 
			
		||||
            <span class="title-label">Loading...</span>
 | 
			
		||||
        </span>
 | 
			
		||||
    </li>
 | 
			
		||||
    <li ng-repeat="child in composition">
 | 
			
		||||
    <li ng-repeat="child in ::composition">
 | 
			
		||||
        <mct-representation key="'tree-node'"
 | 
			
		||||
                            mct-object="child"
 | 
			
		||||
                            parameters="parameters"
 | 
			
		||||
                            ng-model="ngModel">
 | 
			
		||||
                            mct-object="::child"
 | 
			
		||||
                            parameters="::parameters"
 | 
			
		||||
                            mct-model="::mctModel">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
    </li>
 | 
			
		||||
</ul>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,15 +19,15 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<span ng-controller="ToggleController as toggle">
 | 
			
		||||
    <span ng-controller="TreeNodeController as treeNode">
 | 
			
		||||
 | 
			
		||||
<span ng-controller="TreeNodeController as treeNode">
 | 
			
		||||
    <span mct-refresh="treeNode.listen(callback)">
 | 
			
		||||
        <span
 | 
			
		||||
            class="tree-item menus-to-left"
 | 
			
		||||
            ng-class="{selected: treeNode.isSelected()}"
 | 
			
		||||
            >
 | 
			
		||||
            ng-class="::{selected: treeNode.isSelected()}">
 | 
			
		||||
            <span
 | 
			
		||||
                class='ui-symbol view-control flex-elem'
 | 
			
		||||
                ng-class="{ 'has-children': model.composition !== undefined, expanded: toggle.isActive() }"
 | 
			
		||||
                ng-class="::{ 'has-children': model.composition !== undefined, expanded: toggle.isActive() }"
 | 
			
		||||
                ng-click="toggle.toggle(); treeNode.trackExpansion()"
 | 
			
		||||
                >
 | 
			
		||||
            </span>
 | 
			
		||||
@@ -35,21 +35,21 @@
 | 
			
		||||
                class="rep-object-label"
 | 
			
		||||
                key="'label'"
 | 
			
		||||
                mct-object="domainObject"
 | 
			
		||||
                parameters="{suppressMenuOnEdit: true}"
 | 
			
		||||
                parameters="::{suppressMenuOnEdit: true}"
 | 
			
		||||
                ng-click="treeNode.select()"
 | 
			
		||||
                >
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
        </span>
 | 
			
		||||
        <span
 | 
			
		||||
            class="tree-item-subtree"
 | 
			
		||||
            ng-show="toggle.isActive()"
 | 
			
		||||
            ng-if="model.composition !== undefined"
 | 
			
		||||
            ng-if="::treeNode.isExpanded()"
 | 
			
		||||
            >
 | 
			
		||||
 | 
			
		||||
            <mct-representation key="'subtree'"
 | 
			
		||||
                                ng-model="ngModel"
 | 
			
		||||
                                parameters="parameters"
 | 
			
		||||
                                mct-object="treeNode.hasBeenExpanded() && domainObject">
 | 
			
		||||
                                mct-model="::mctModel"
 | 
			
		||||
                                parameters="::parameters"
 | 
			
		||||
                                mct-object="::(treeNode.hasBeenExpanded() && domainObject)"
 | 
			
		||||
                                >
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
 | 
			
		||||
        </span>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,9 @@
 | 
			
		||||
<ul class="tree">
 | 
			
		||||
    <li>
 | 
			
		||||
        <mct-representation key="'tree-node'"
 | 
			
		||||
                            mct-object="domainObject"
 | 
			
		||||
                            ng-model="ngModel"
 | 
			
		||||
                            parameters="parameters">
 | 
			
		||||
                            mct-object="::domainObject"
 | 
			
		||||
                            mct-model="::mctModel"
 | 
			
		||||
                            parameters="::parameters">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
    </li>
 | 
			
		||||
</ul>
 | 
			
		||||
 
 | 
			
		||||
@@ -97,6 +97,7 @@ define(
 | 
			
		||||
                    navContext = navObject &&
 | 
			
		||||
                            navObject.getCapability('context'),
 | 
			
		||||
                    nodePath,
 | 
			
		||||
                    wasSelected = self.isSelected(),
 | 
			
		||||
                    navPath;
 | 
			
		||||
 | 
			
		||||
                // Deselect; we will reselect below, iff we are
 | 
			
		||||
@@ -121,15 +122,17 @@ define(
 | 
			
		||||
                        // 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();
 | 
			
		||||
                        } else if (!self.isExpanded()) {
 | 
			
		||||
                            // node path is shorter: Expand!
 | 
			
		||||
                            self.toggle();
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (self.isSelected() !== wasSelected) {
 | 
			
		||||
                    self.notifyListener();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Callback for the selection updates; track the currently
 | 
			
		||||
@@ -139,16 +142,53 @@ define(
 | 
			
		||||
                checkSelection();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.isExpandedFlag = false;
 | 
			
		||||
            this.isSelectedFlag = false;
 | 
			
		||||
            this.hasBeenExpandedFlag = false;
 | 
			
		||||
            this.$timeout = $timeout;
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
 | 
			
		||||
            checkSelection();
 | 
			
		||||
 | 
			
		||||
            // Listen for changes which will effect display parameters
 | 
			
		||||
            $scope.$watch("ngModel.selectedObject", setSelection);
 | 
			
		||||
            $scope.$watch("domainObject", checkSelection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TreeNodeController.prototype.notifyListener = function () {
 | 
			
		||||
            if (this.listener) {
 | 
			
		||||
                this.listener();
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        TreeNodeController.prototype.isExpanded = function () {
 | 
			
		||||
            return this.isExpandedFlag;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        TreeNodeController.prototype.toggle = function () {
 | 
			
		||||
            this.isExpandedFlag = !this.isExpandedFlag;
 | 
			
		||||
            if (this.isExpanded() && !this.hasBeenExpanded()) {
 | 
			
		||||
                this.trackExpansion();
 | 
			
		||||
            }
 | 
			
		||||
            this.notifyListener();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        TreeNodeController.prototype.listen = function (callback) {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                domainObject = this.$scope.domainObject,
 | 
			
		||||
                unlistenToMutation;
 | 
			
		||||
 | 
			
		||||
            this.listener = callback;
 | 
			
		||||
            unlistenToMutation = domainObject.getCapability('mutation')
 | 
			
		||||
                .listen(callback);
 | 
			
		||||
 | 
			
		||||
            return function () {
 | 
			
		||||
                unlistenToMutation();
 | 
			
		||||
                if (self.listener === callback) {
 | 
			
		||||
                    delete self.listener;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Select the domain object represented by this node in the tree.
 | 
			
		||||
         * This will both update the `selectedObject` property in
 | 
			
		||||
@@ -178,6 +218,7 @@ define(
 | 
			
		||||
                // want this to be spread across multiple digest cycles.
 | 
			
		||||
                self.$timeout(function () {
 | 
			
		||||
                    self.hasBeenExpandedFlag = true;
 | 
			
		||||
                    self.notifyListener();
 | 
			
		||||
                }, 0);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								platform/commonUI/general/src/directives/MCTRefresh.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								platform/commonUI/general/src/directives/MCTRefresh.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web 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 Web 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define*/
 | 
			
		||||
define(
 | 
			
		||||
    ['angular'],
 | 
			
		||||
    function (angular) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The `mct-refresh` directive may be used to explicitly
 | 
			
		||||
         * trigger the refresh of the contents of the HTML element
 | 
			
		||||
         * which has this attribute. When used in combination with
 | 
			
		||||
         * one-time binding, this allows templates (or sections thereof)
 | 
			
		||||
         * to eschew watches and instead use other strategies for
 | 
			
		||||
         * change detection.
 | 
			
		||||
         *
 | 
			
		||||
         * The `mct-refresh` directive is applied as an attribute
 | 
			
		||||
         * whose value should be an Angular expression which:
 | 
			
		||||
         *
 | 
			
		||||
         * * Will be evaluated with a variable `callback`, which is
 | 
			
		||||
         *   a function that, when invoked, will trigger a refresh.
 | 
			
		||||
         * * May return a function which will be invoked by `mct-refresh`
 | 
			
		||||
         *   when the directive is no longer applicable; this should
 | 
			
		||||
         *   be used to release any resources associated with the
 | 
			
		||||
         *   above callback.
 | 
			
		||||
         *
 | 
			
		||||
         * Example usage:
 | 
			
		||||
         *
 | 
			
		||||
         * ```
 | 
			
		||||
         * <span mct-refresh="someObservable.observe(callback)">
 | 
			
		||||
         *     <div>{{::someObservable.getValue()}}</div>
 | 
			
		||||
         * </span>
 | 
			
		||||
         * ```
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/general
 | 
			
		||||
         */
 | 
			
		||||
        function MCTRefresh() {
 | 
			
		||||
 | 
			
		||||
            function link(scope, elem, attrs, ctrl, transclude) {
 | 
			
		||||
                var unlisten;
 | 
			
		||||
 | 
			
		||||
                function recreateContents() {
 | 
			
		||||
                    transclude(function (clone) {
 | 
			
		||||
                        elem.empty();
 | 
			
		||||
                        elem.append(clone);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                recreateContents();
 | 
			
		||||
 | 
			
		||||
                unlisten = scope.$eval(
 | 
			
		||||
                    attrs.mctRefresh,
 | 
			
		||||
                    { callback: recreateContents }
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                if (angular.isFunction(unlisten)) {
 | 
			
		||||
                    scope.$on("$destroy", unlisten);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                restrict: "A",
 | 
			
		||||
                transclude: true,
 | 
			
		||||
                link: link
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MCTRefresh;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										122
									
								
								platform/commonUI/general/test/directives/MCTRefreshSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								platform/commonUI/general/test/directives/MCTRefreshSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web 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 Web 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/directives/MCTRefresh"],
 | 
			
		||||
    function (MCTRefresh) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The mct-refresh directive", function () {
 | 
			
		||||
            var mctRefresh;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mctRefresh = new MCTRefresh();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is applicable as an attribute only", function () {
 | 
			
		||||
                expect(mctRefresh.restrict).toEqual("A");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when linked", function () {
 | 
			
		||||
                var mockScope,
 | 
			
		||||
                    mockElement,
 | 
			
		||||
                    testAttrs,
 | 
			
		||||
                    mockTransclude,
 | 
			
		||||
                    mockClone,
 | 
			
		||||
                    mockUnlisten;
 | 
			
		||||
 | 
			
		||||
                function fireEvent(event) {
 | 
			
		||||
                    mockScope.$on.calls.forEach(function (call) {
 | 
			
		||||
                        if (call.args[0] === event) {
 | 
			
		||||
                            call.args[1]();
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockScope = jasmine.createSpyObj(
 | 
			
		||||
                        "$scope",
 | 
			
		||||
                        [ "$eval", "$on", "$apply" ]
 | 
			
		||||
                    );
 | 
			
		||||
                    mockElement = jasmine.createSpyObj(
 | 
			
		||||
                        "elem",
 | 
			
		||||
                        [ "empty", "append" ]
 | 
			
		||||
                    );
 | 
			
		||||
                    testAttrs = { mctRefresh: "some-expr" };
 | 
			
		||||
                    mockTransclude = jasmine.createSpy();
 | 
			
		||||
                    mockTransclude.andCallFake(function (fn) {
 | 
			
		||||
                        fn(mockClone);
 | 
			
		||||
                    });
 | 
			
		||||
                    mockClone = jasmine.createSpyObj(
 | 
			
		||||
                        "elem",
 | 
			
		||||
                        [ "empty", "append" ]
 | 
			
		||||
                    );
 | 
			
		||||
                    mockUnlisten = jasmine.createSpy();
 | 
			
		||||
 | 
			
		||||
                    mockScope.$eval.andReturn(mockUnlisten);
 | 
			
		||||
 | 
			
		||||
                    mctRefresh.link(
 | 
			
		||||
                        mockScope,
 | 
			
		||||
                        mockElement,
 | 
			
		||||
                        testAttrs,
 | 
			
		||||
                        {},
 | 
			
		||||
                        mockTransclude
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("adds its transcluded content", function () {
 | 
			
		||||
                    expect(mockElement.append)
 | 
			
		||||
                        .toHaveBeenCalledWith(mockClone);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("passes a callback into its associated expression", function () {
 | 
			
		||||
                    expect(mockScope.$eval).toHaveBeenCalledWith(
 | 
			
		||||
                        testAttrs.mctRefresh,
 | 
			
		||||
                        { callback: jasmine.any(Function) }
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and triggered via callback", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        mockScope.$eval.mostRecentCall.args[1].callback();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("transcludes its content again", function () {
 | 
			
		||||
                        expect(mockTransclude.calls.length).toEqual(2);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and then destroyed", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        fireEvent("$destroy");
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("stops listening", function () {
 | 
			
		||||
                        expect(mockUnlisten).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
    "directives/MCTContainer",
 | 
			
		||||
    "directives/MCTDrag",
 | 
			
		||||
    "directives/MCTPopup",
 | 
			
		||||
    "directives/MCTRefresh",
 | 
			
		||||
    "directives/MCTResize",
 | 
			
		||||
    "directives/MCTScroll",
 | 
			
		||||
    "directives/MCTSplitPane",
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@
 | 
			
		||||
 * Module defining MCTInclude. Created by vwoeltje on 11/7/14.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
    ["./OneWayBinder"],
 | 
			
		||||
    function (OneWayBinder) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -35,19 +35,26 @@ define(
 | 
			
		||||
         * key which can be exposed by bundles, instead of requiring
 | 
			
		||||
         * an explicit path.
 | 
			
		||||
         *
 | 
			
		||||
         * This directive uses two-way binding for three attributes:
 | 
			
		||||
         * This directive uses the following attributes:
 | 
			
		||||
         *
 | 
			
		||||
         * * `key`, matched against the key of a defined template extension
 | 
			
		||||
         *   in order to determine which actual template to include.
 | 
			
		||||
         * * `ng-model`, populated as `ngModel` in the loaded template's
 | 
			
		||||
         *   scope; used for normal ng-model purposes (e.g. if the
 | 
			
		||||
         *   included template is meant to two-way bind to a data model.)
 | 
			
		||||
         * * `parameters`, used to communicate display parameters to
 | 
			
		||||
         *   the included template (e.g. title.) The difference between
 | 
			
		||||
         *   `parameters` and `ngModel` is intent: Both are two-way
 | 
			
		||||
         *   bound, but `ngModel` is useful for data models (more like
 | 
			
		||||
         *   an output) and `parameters` is meant to be useful for
 | 
			
		||||
         *   display parameterization (more like an input.)
 | 
			
		||||
         * * `key`: An Angular expression, matched against the key of a
 | 
			
		||||
         *   defined template extension in order to determine which actual
 | 
			
		||||
         *   template to include.
 | 
			
		||||
         * * `mct-model`: An Angular expression; its value is watched
 | 
			
		||||
         *   and passed into the template's scope as property `mctModel`.
 | 
			
		||||
         * * `parameters`: An Angular expression; its value is watched
 | 
			
		||||
         *   and passed into the template's scope as property `parameters`.
 | 
			
		||||
         *
 | 
			
		||||
         * The difference between `parameters` and `mct-model` is intent;
 | 
			
		||||
         * `parameters` should be used for display-time parameters which
 | 
			
		||||
         * are not meant to be changed, whereas `mct-model` should be
 | 
			
		||||
         * used to pass in objects whose properties will (or may) be
 | 
			
		||||
         * modified by the included template.
 | 
			
		||||
         *
 | 
			
		||||
         * (For backwards compatibility, `ng-model` is treated identically
 | 
			
		||||
         * to `mct-model`, and the property `ngModel` will be provided
 | 
			
		||||
         * in scope with the same value as `mctModel`. This usage is
 | 
			
		||||
         * deprecated and should be avoided.)
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/representation
 | 
			
		||||
         * @constructor
 | 
			
		||||
@@ -57,14 +64,24 @@ define(
 | 
			
		||||
        function MCTInclude(templates, templateLinker) {
 | 
			
		||||
            var templateMap = {};
 | 
			
		||||
 | 
			
		||||
            function link(scope, element) {
 | 
			
		||||
                var changeTemplate = templateLinker.link(
 | 
			
		||||
                    scope,
 | 
			
		||||
                    element,
 | 
			
		||||
                    scope.key && templateMap[scope.key]
 | 
			
		||||
                );
 | 
			
		||||
            function link(scope, element, attrs) {
 | 
			
		||||
                var parent = scope.$parent,
 | 
			
		||||
                    key = parent.$eval(attrs.key),
 | 
			
		||||
                    changeTemplate = templateLinker.link(
 | 
			
		||||
                        scope,
 | 
			
		||||
                        element,
 | 
			
		||||
                        key && templateMap[key]
 | 
			
		||||
                    ),
 | 
			
		||||
                    binder = new OneWayBinder(scope, attrs);
 | 
			
		||||
 | 
			
		||||
                scope.$watch('key', function (key) {
 | 
			
		||||
                binder.bind('ngModel');
 | 
			
		||||
                binder.bind('mctModel');
 | 
			
		||||
                binder.bind('parameters');
 | 
			
		||||
 | 
			
		||||
                binder.alias('ngModel', 'mctModel');
 | 
			
		||||
                binder.alias('mctModel', 'ngModel');
 | 
			
		||||
 | 
			
		||||
                binder.watch('key', function (key) {
 | 
			
		||||
                    changeTemplate(key && templateMap[key]);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@@ -87,8 +104,8 @@ define(
 | 
			
		||||
                // May hide the element, so let other directives act first
 | 
			
		||||
                priority: -1000,
 | 
			
		||||
 | 
			
		||||
                // Two-way bind key, ngModel, and parameters
 | 
			
		||||
                scope: { key: "=", ngModel: "=", parameters: "=" }
 | 
			
		||||
                // Isolate this scope; do not inherit properties from parent
 | 
			
		||||
                scope: {}
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,26 +27,40 @@
 | 
			
		||||
 * @namespace platform/representation
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
    ["./OneWayBinder"],
 | 
			
		||||
    function (OneWayBinder) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Defines the mct-representation directive. This may be used to
 | 
			
		||||
         * present domain objects as HTML (with event wiring), with the
 | 
			
		||||
         * specific representation being mapped to a defined extension
 | 
			
		||||
         * (as defined in either the `representation` category-of-extension,
 | 
			
		||||
         * (as defined in either the `representations` category-of-extension,
 | 
			
		||||
         * or the `views` category-of-extension.)
 | 
			
		||||
         *
 | 
			
		||||
         * This directive uses two-way binding for three attributes:
 | 
			
		||||
         *
 | 
			
		||||
         * * `key`, matched against the key of a defined template extension
 | 
			
		||||
         *   in order to determine which actual template to include.
 | 
			
		||||
         * * `mct-object`, populated as `domainObject` in the loaded
 | 
			
		||||
         *   template's scope. This is the domain object being
 | 
			
		||||
         *   represented as HTML by this directive.
 | 
			
		||||
         * * `parameters`, used to communicate display parameters to
 | 
			
		||||
         *   the included template (e.g. title.)
 | 
			
		||||
         * * `key`: An Angular expression, matched against the key of a
 | 
			
		||||
         *   defined representation or view extension in order to determine
 | 
			
		||||
         *   which actual template to include.
 | 
			
		||||
         * * `mct-model`: An Angular expression; its value is watched
 | 
			
		||||
         *   and passed into the template's scope as property `mctModel`.
 | 
			
		||||
         * * `parameters`: An Angular expression; its value is watched
 | 
			
		||||
         *   and passed into the template's scope as property `parameters`.
 | 
			
		||||
         *
 | 
			
		||||
         * The difference between `parameters` and `mct-model` is intent;
 | 
			
		||||
         * `parameters` should be used for display-time parameters which
 | 
			
		||||
         * are not meant to be changed, whereas `mct-model` should be
 | 
			
		||||
         * used to pass in objects whose properties will (or may) be
 | 
			
		||||
         * modified by the included representation.
 | 
			
		||||
         *
 | 
			
		||||
         * (For backwards compatibility, `ng-model` is treated identically
 | 
			
		||||
         * to `mct-model`, and the property `ngModel` will be provided
 | 
			
		||||
         * in scope with the same value as `mctModel`. This usage is
 | 
			
		||||
         * deprecated and should be avoided.)
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/representation
 | 
			
		||||
         * @constructor
 | 
			
		||||
@@ -94,7 +108,8 @@ define(
 | 
			
		||||
                    couldEdit = false,
 | 
			
		||||
                    lastIdPath = [],
 | 
			
		||||
                    lastKey,
 | 
			
		||||
                    changeTemplate = templateLinker.link($scope, element);
 | 
			
		||||
                    changeTemplate = templateLinker.link($scope, element),
 | 
			
		||||
                    binder = new OneWayBinder($scope, attrs);
 | 
			
		||||
 | 
			
		||||
                // Populate scope with any capabilities indicated by the
 | 
			
		||||
                // representation's extension definition
 | 
			
		||||
@@ -236,13 +251,20 @@ define(
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                binder.bind('parameters');
 | 
			
		||||
                binder.bind('mctModel');
 | 
			
		||||
                binder.bind('ngModel');
 | 
			
		||||
 | 
			
		||||
                binder.alias('ngModel', 'mctModel');
 | 
			
		||||
                binder.alias('mctModel', 'ngModel');
 | 
			
		||||
 | 
			
		||||
                // Update the representation when the key changes (e.g. if a
 | 
			
		||||
                // different representation has been selected)
 | 
			
		||||
                $scope.$watch("key", refresh);
 | 
			
		||||
                binder.bind('key', refresh);
 | 
			
		||||
 | 
			
		||||
                // Also update when the represented domain object changes
 | 
			
		||||
                // (to a different object)
 | 
			
		||||
                $scope.$watch("domainObject", refresh);
 | 
			
		||||
                binder.alias('mctObject', 'domainObject', refresh);
 | 
			
		||||
 | 
			
		||||
                // Finally, also update when there is a new version of that
 | 
			
		||||
                // same domain object; these changes should be tracked in the
 | 
			
		||||
@@ -270,14 +292,8 @@ define(
 | 
			
		||||
                // May hide the element, so let other directives act first
 | 
			
		||||
                priority: -1000,
 | 
			
		||||
 | 
			
		||||
                // Two-way bind key and parameters, get the represented domain
 | 
			
		||||
                // object as "mct-object"
 | 
			
		||||
                scope: {
 | 
			
		||||
                    key: "=",
 | 
			
		||||
                    domainObject: "=mctObject",
 | 
			
		||||
                    ngModel: "=",
 | 
			
		||||
                    parameters: "="
 | 
			
		||||
                }
 | 
			
		||||
                // Isolate this scope
 | 
			
		||||
                scope: {}
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										113
									
								
								platform/representation/src/OneWayBinder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								platform/representation/src/OneWayBinder.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web 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 Web 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Supports the "one-way" binding behavior of `mct-representation`
 | 
			
		||||
         * and `mct-include`; watches expression associated with attributes
 | 
			
		||||
         * in a parent scope, then passes these into the child scope when
 | 
			
		||||
         * they change (but does not assign anything back to the parent
 | 
			
		||||
         * scope if the child changes.)
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/representation
 | 
			
		||||
         * @param scope the Angular scope to watch
 | 
			
		||||
         * @param attrs the relevant attributes, as passed into the `link`
 | 
			
		||||
         *              function of the relevant directive
 | 
			
		||||
         */
 | 
			
		||||
        function OneWayBinder(scope, attrs) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            this.unwatches = [];
 | 
			
		||||
            this.scope = scope;
 | 
			
		||||
            this.parent = scope.$parent;
 | 
			
		||||
            this.attrs = attrs;
 | 
			
		||||
 | 
			
		||||
            // Detach any listeners from the parent
 | 
			
		||||
            scope.$on('$destroy', function () {
 | 
			
		||||
                self.unwatches.forEach(function (unwatch) {
 | 
			
		||||
                    unwatch();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * One-way bind an attribute. The value of the named attribute will
 | 
			
		||||
         * be watched as an Angular expression in the parent scope; its
 | 
			
		||||
         * value will be exposed in the child scope as a property of
 | 
			
		||||
         * the same name.
 | 
			
		||||
         * @param {string} attr the name of the attribute to watch
 | 
			
		||||
         * @param {Function} [callback] a callback to invoke with new values
 | 
			
		||||
         */
 | 
			
		||||
        OneWayBinder.prototype.bind = function (attr, callback) {
 | 
			
		||||
            this.alias(attr, attr, callback);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * One-way bind an attribute. As `bind`, but allows the property
 | 
			
		||||
         * name in the child scope used to expose these values to be
 | 
			
		||||
         * specified as something different from the attribute name.
 | 
			
		||||
         * @param {string} attr the name of the attribute to watch
 | 
			
		||||
         * @param {string} property the name of the property to use in scope
 | 
			
		||||
         * @param {Function} [callback] a callback to invoke with new values
 | 
			
		||||
         */
 | 
			
		||||
        OneWayBinder.prototype.alias = function (attr, property, callback) {
 | 
			
		||||
            var scope = this.scope;
 | 
			
		||||
 | 
			
		||||
            this.watch(attr, function expose(value) {
 | 
			
		||||
                scope[property] = value;
 | 
			
		||||
                if (callback) {
 | 
			
		||||
                    callback(value);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Expose in scope immediately, similar to scope: { attr: "=" }
 | 
			
		||||
            // in a directive definition object.
 | 
			
		||||
            scope[property] = this.parent.$eval(this.attrs[attr]);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Watch for changes in this attribute. The named attribute's value
 | 
			
		||||
         * will be watched as an Angular expression in the parent scope,
 | 
			
		||||
         * and the provided callback will be invoked with the value of that
 | 
			
		||||
         * expression when changes occur.
 | 
			
		||||
         * @param {string} attr the name of the attribute to watch
 | 
			
		||||
         * @param {Function} callback the callback to invoke with new values
 | 
			
		||||
         */
 | 
			
		||||
        OneWayBinder.prototype.watch = function (attr, callback) {
 | 
			
		||||
            var expr = this.attrs[attr];
 | 
			
		||||
            if (expr) {
 | 
			
		||||
                this.unwatches.push(this.parent.$watch(
 | 
			
		||||
                    expr,
 | 
			
		||||
                    callback,
 | 
			
		||||
                    expr && expr[0] === '{'
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return OneWayBinder;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -35,11 +35,12 @@ define(
 | 
			
		||||
                mockLinker,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockElement,
 | 
			
		||||
                testAttrs,
 | 
			
		||||
                mockChangeTemplate,
 | 
			
		||||
                mctInclude;
 | 
			
		||||
 | 
			
		||||
            function fireWatch(expr, value) {
 | 
			
		||||
                mockScope.$watch.calls.forEach(function (call) {
 | 
			
		||||
                mockScope.$parent.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](value);
 | 
			
		||||
                    }
 | 
			
		||||
@@ -68,6 +69,8 @@ define(
 | 
			
		||||
                    ['link', 'getPath']
 | 
			
		||||
                );
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
 | 
			
		||||
                mockScope.$parent =
 | 
			
		||||
                    jasmine.createSpyObj('parent', ['$watch', '$eval']);
 | 
			
		||||
                mockElement = jasmine.createSpyObj('element', ['empty']);
 | 
			
		||||
                mockChangeTemplate = jasmine.createSpy('changeTemplate');
 | 
			
		||||
                mockLinker.link.andReturn(mockChangeTemplate);
 | 
			
		||||
@@ -75,7 +78,12 @@ define(
 | 
			
		||||
                    return testUrls[template.key];
 | 
			
		||||
                });
 | 
			
		||||
                mctInclude = new MCTInclude(testTemplates, mockLinker);
 | 
			
		||||
                mctInclude.link(mockScope, mockElement, {});
 | 
			
		||||
                testAttrs = {
 | 
			
		||||
                    key: "parentKey",
 | 
			
		||||
                    mctModel: "someExpr",
 | 
			
		||||
                    ngModel: "someOtherExpr"
 | 
			
		||||
                };
 | 
			
		||||
                mctInclude.link(mockScope, mockElement, testAttrs);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is restricted to elements", function () {
 | 
			
		||||
@@ -88,17 +96,28 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("reads a template location from a scope's key variable", function () {
 | 
			
		||||
                mockScope.key = 'abc';
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch(testAttrs.key, 'abc');
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testTemplates[0]);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = 'xyz';
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch(testAttrs.key, 'xyz');
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testTemplates[1]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches for changes on both ng-model and mct-model", function () {
 | 
			
		||||
                expect(mockScope.$parent.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    testAttrs.ngModel,
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
                expect(mockScope.$parent.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    testAttrs.mctModel,
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,7 @@ define(
 | 
			
		||||
                mockChangeTemplate,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockElement,
 | 
			
		||||
                testAttrs,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                testModel,
 | 
			
		||||
                mctRepresentation;
 | 
			
		||||
@@ -57,7 +58,7 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function fireWatch(expr, value) {
 | 
			
		||||
                mockScope.$watch.calls.forEach(function (call) {
 | 
			
		||||
                mockScope.$parent.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](value);
 | 
			
		||||
                    }
 | 
			
		||||
@@ -102,6 +103,13 @@ define(
 | 
			
		||||
                    testUrls[t.key] = "some URL " + String(i);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                testAttrs = {
 | 
			
		||||
                    "mctObject": "someExpr",
 | 
			
		||||
                    "key": "someOtherExpr",
 | 
			
		||||
                    "ngModel": "yetAnotherExpr",
 | 
			
		||||
                    "mctModel": "theExprsKeepOnComing"
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mockRepresenters = ["A", "B"].map(function (name) {
 | 
			
		||||
                    var constructor = jasmine.createSpy("Representer" + name),
 | 
			
		||||
                        representer = jasmine.createSpyObj(
 | 
			
		||||
@@ -121,6 +129,8 @@ define(
 | 
			
		||||
                mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
 | 
			
		||||
 | 
			
		||||
                mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
 | 
			
		||||
                mockScope.$parent =
 | 
			
		||||
                    jasmine.createSpyObj('parent', ['$watch', '$eval']);
 | 
			
		||||
                mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
 | 
			
		||||
 | 
			
		||||
@@ -138,7 +148,7 @@ define(
 | 
			
		||||
                    mockLinker,
 | 
			
		||||
                    mockLog
 | 
			
		||||
                );
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement, testAttrs);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is restricted to elements", function () {
 | 
			
		||||
@@ -150,15 +160,7 @@ define(
 | 
			
		||||
                    .toHaveBeenCalledWith(mockScope, mockElement);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches scope when linked", function () {
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "key",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
                );
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
                );
 | 
			
		||||
            it("watches for model changes when linked", function () {
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "domainObject.getModel().modified",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
@@ -166,24 +168,16 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("recognizes keys for representations", function () {
 | 
			
		||||
                mockScope.key = "abc";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch('domainObject', mockDomainObject);
 | 
			
		||||
                fireWatch(testAttrs.key, "abc");
 | 
			
		||||
                fireWatch(testAttrs.mctObject, mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testRepresentations[0]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("recognizes keys for views", function () {
 | 
			
		||||
                mockScope.key = "xyz";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
                // Trigger the watches
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch('domainObject', mockDomainObject);
 | 
			
		||||
                fireWatch(testAttrs.key, "xyz");
 | 
			
		||||
                fireWatch(testAttrs.mctObject, mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testViews[1]);
 | 
			
		||||
@@ -192,25 +186,20 @@ define(
 | 
			
		||||
            it("does not load templates until there is an object", function () {
 | 
			
		||||
                mockScope.key = "xyz";
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch(testAttrs.key, "xyz");
 | 
			
		||||
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .not.toHaveBeenCalledWith(jasmine.any(Object));
 | 
			
		||||
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
                fireWatch('domainObject', mockDomainObject);
 | 
			
		||||
                fireWatch(testAttrs.mctObject, mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(Object));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("loads declared capabilities", function () {
 | 
			
		||||
                mockScope.key = "def";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                fireWatch(testAttrs.key, "def");
 | 
			
		||||
                fireWatch(testAttrs.mctObject, mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockDomainObject.useCapability)
 | 
			
		||||
                    .toHaveBeenCalledWith("testCapability");
 | 
			
		||||
@@ -219,35 +208,43 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("logs when no representation is available for a key", function () {
 | 
			
		||||
                mockScope.key = "someUnknownThing";
 | 
			
		||||
 | 
			
		||||
                // Verify precondition
 | 
			
		||||
                expect(mockLog.warn).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                fireWatch(testAttrs.key, "someUnkownThing");
 | 
			
		||||
 | 
			
		||||
                // Should have gotten a warning - that's an unknown key
 | 
			
		||||
                expect(mockLog.warn).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("clears out obsolete peroperties from scope", function () {
 | 
			
		||||
                mockScope.key = "def";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
                mockDomainObject.useCapability.andReturn("some value");
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                fireWatch(testAttrs.key, "def");
 | 
			
		||||
                fireWatch(testAttrs.mctObject, mockDomainObject);
 | 
			
		||||
                expect(mockScope.testCapability).toBeDefined();
 | 
			
		||||
 | 
			
		||||
                // Change the view
 | 
			
		||||
                mockScope.key = "xyz";
 | 
			
		||||
                // Change the view; should clear capabilities from scope
 | 
			
		||||
                fireWatch(testAttrs.key, "xyz");
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch again; should clear capability from scope
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                expect(mockScope.testCapability).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches for changes on both ng-model and mct-model", function () {
 | 
			
		||||
                expect(mockScope.$parent.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    testAttrs.ngModel,
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
                expect(mockScope.$parent.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    testAttrs.mctModel,
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("detects changes among linked instances", function () {
 | 
			
		||||
                var mockContext = jasmine.createSpyObj('context', ['getPath']),
 | 
			
		||||
                    mockContext2 = jasmine.createSpyObj('context', ['getPath']),
 | 
			
		||||
@@ -295,6 +292,19 @@ define(
 | 
			
		||||
                expect(mockChangeTemplate.calls.length)
 | 
			
		||||
                    .toEqual(callCount + 1);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches for changes on both ng-model and mct-model", function () {
 | 
			
		||||
                expect(mockScope.$parent.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    testAttrs.ngModel,
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
                expect(mockScope.$parent.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    testAttrs.mctModel,
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										177
									
								
								platform/representation/test/OneWayBinderSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								platform/representation/test/OneWayBinderSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web 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 Web 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/OneWayBinder"],
 | 
			
		||||
    function (OneWayBinder) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("OneWayBinder", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                testAttrs,
 | 
			
		||||
                testValues,
 | 
			
		||||
                mockUnwatches,
 | 
			
		||||
                binder;
 | 
			
		||||
 | 
			
		||||
            function fireEvent(event) {
 | 
			
		||||
                mockScope.$on.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === event) {
 | 
			
		||||
                        call.args[1]();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function fireParentWatch(expr) {
 | 
			
		||||
                mockScope.$parent.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](mockScope.$parent.$eval(expr));
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockUnwatches = [];
 | 
			
		||||
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$on']);
 | 
			
		||||
                mockScope.$parent = jasmine.createSpyObj(
 | 
			
		||||
                    '$parent',
 | 
			
		||||
                    [ '$watch', '$eval' ]
 | 
			
		||||
                );
 | 
			
		||||
                testAttrs = { a: 'attrA', b: 'attrB', c: 'attrC' };
 | 
			
		||||
                testValues = { attrA: 42, attrB: ['foo'], attrC: { a: 0 } };
 | 
			
		||||
 | 
			
		||||
                mockScope.$parent.$eval.andCallFake(function (expr) {
 | 
			
		||||
                    return testValues[expr];
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockScope.$parent.$watch.andCallFake(function () {
 | 
			
		||||
                    var mockUnwatch = jasmine.createSpy();
 | 
			
		||||
                    mockUnwatches.push(mockUnwatch);
 | 
			
		||||
                    return mockUnwatch;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                binder = new OneWayBinder(mockScope, testAttrs);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("bind", function () {
 | 
			
		||||
                var attrNames;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    attrNames = Object.keys(testAttrs);
 | 
			
		||||
                    attrNames.forEach(function (attr) {
 | 
			
		||||
                        binder.bind(attr);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("exposes values from the parent in scope", function () {
 | 
			
		||||
                    attrNames.forEach(function (attr) {
 | 
			
		||||
                        expect(mockScope[attr])
 | 
			
		||||
                            .toEqual(testValues[testAttrs[attr]]);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates values from the parent in scope", function () {
 | 
			
		||||
                    var oldValues = testValues,
 | 
			
		||||
                        newValues = {};
 | 
			
		||||
                    Object.keys(oldValues).forEach(function (key) {
 | 
			
		||||
                        newValues[key] = oldValues[key] + " a change";
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    testValues = newValues;
 | 
			
		||||
 | 
			
		||||
                    attrNames.forEach(function (attr) {
 | 
			
		||||
                        expect(mockScope[attr])
 | 
			
		||||
                            .toEqual(oldValues[testAttrs[attr]]);
 | 
			
		||||
                        fireParentWatch(testAttrs[attr]);
 | 
			
		||||
                        expect(mockScope[attr])
 | 
			
		||||
                            .toEqual(newValues[testAttrs[attr]]);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("attaches one watch per attribute", function () {
 | 
			
		||||
                    expect(mockUnwatches.length).toEqual(3);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("alias", function () {
 | 
			
		||||
                var attrNames;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    binder.alias('a', 'someAlias');
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("exposes values under a different name", function () {
 | 
			
		||||
                    expect(mockScope.someAlias).toEqual(testValues.attrA);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates values under a different name", function () {
 | 
			
		||||
                    var newValue = "some new value";
 | 
			
		||||
                    testValues.attrA = newValue;
 | 
			
		||||
                    expect(mockScope.someAlias).not.toEqual(newValue);
 | 
			
		||||
                    fireParentWatch(testAttrs.a);
 | 
			
		||||
                    expect(mockScope.someAlias).toEqual(newValue);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("watch", function () {
 | 
			
		||||
                var mockCallback = jasmine.createSpy();
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    binder.watch('b', mockCallback);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("invokes callbacks when values change", function () {
 | 
			
		||||
                    var newValue = "some new value";
 | 
			
		||||
                    testValues.attrB = newValue;
 | 
			
		||||
                    expect(mockCallback).not.toHaveBeenCalled();
 | 
			
		||||
                    fireParentWatch(testAttrs.b);
 | 
			
		||||
                    expect(mockCallback).toHaveBeenCalledWith(newValue);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("generally watches for reference equality", function () {
 | 
			
		||||
                    expect(mockScope.$parent.$watch.mostRecentCall.args[2])
 | 
			
		||||
                        .toBeFalsy();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("watches for equivalence when expressions are anonymous objects", function () {
 | 
			
		||||
                    testAttrs.d = "{ a: 'foo' }";
 | 
			
		||||
                    binder.watch('d', mockCallback);
 | 
			
		||||
                    expect(mockScope.$parent.$watch.mostRecentCall.args[2])
 | 
			
		||||
                        .toBeTruthy();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("releases watches from parent when scope is destroyed", function () {
 | 
			
		||||
                binder.bind('a');
 | 
			
		||||
                binder.alias('b', 'xyz');
 | 
			
		||||
                binder.watch('c', jasmine.createSpy());
 | 
			
		||||
                fireEvent('$destroy');
 | 
			
		||||
                mockUnwatches.forEach(function (mockUnwatch) {
 | 
			
		||||
                    expect(mockUnwatch).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -48,10 +48,11 @@ define(function () {
 | 
			
		||||
        this.$scope = $scope;
 | 
			
		||||
        this.searchService = searchService;
 | 
			
		||||
        this.numberToDisplay = this.RESULTS_PER_PAGE;
 | 
			
		||||
        this.availabileResults = 0;
 | 
			
		||||
        this.availableResults = 0;
 | 
			
		||||
        this.$scope.results = [];
 | 
			
		||||
        this.$scope.loading = false;
 | 
			
		||||
        this.pendingQuery = undefined;
 | 
			
		||||
        this.$scope.ngModel = this.$scope.ngModel || {};
 | 
			
		||||
        this.$scope.ngModel.filter = function () {
 | 
			
		||||
            return controller.onFilterChange.apply(controller, arguments);
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,8 @@ requirejs.config({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    waitSeconds: 30,
 | 
			
		||||
 | 
			
		||||
    // dynamically load all test files
 | 
			
		||||
    deps: allTestFiles,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user