[Clocks/Timers] Bring in latest

Merge in latest from master branch to support review of
changes, nasa/openmctweb#207
This commit is contained in:
Victor Woeltjen 2015-10-26 10:42:41 -07:00
commit 20cb2ff239
17 changed files with 564 additions and 106 deletions

View File

@ -1 +1 @@
web: node app.js --port $PORT --include example/localstorage web: node app.js --port $PORT

View File

@ -30,7 +30,8 @@
var CONSTANTS = { var CONSTANTS = {
DIAGRAM_WIDTH: 800, DIAGRAM_WIDTH: 800,
DIAGRAM_HEIGHT: 500 DIAGRAM_HEIGHT: 500
}; },
TOC_HEAD = "# Table of Contents";
GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined
(function () { (function () {
@ -44,6 +45,7 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
split = require("split"), split = require("split"),
stream = require("stream"), stream = require("stream"),
nomnoml = require('nomnoml'), nomnoml = require('nomnoml'),
toc = require("markdown-toc"),
Canvas = require('canvas'), Canvas = require('canvas'),
options = require("minimist")(process.argv.slice(2)); options = require("minimist")(process.argv.slice(2));
@ -110,6 +112,9 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
done(); done();
}; };
transform._flush = function (done) { transform._flush = function (done) {
// Prepend table of contents
markdown =
[ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n");
this.push("<html><body>\n"); this.push("<html><body>\n");
this.push(marked(markdown)); this.push(marked(markdown));
this.push("\n</body></html>\n"); this.push("\n</body></html>\n");
@ -133,8 +138,8 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
customRenderer.link = function (href, title, text) { customRenderer.link = function (href, title, text) {
// ...but only if they look like relative paths // ...but only if they look like relative paths
return (href || "").indexOf(":") === -1 && href[0] !== "/" ? return (href || "").indexOf(":") === -1 && href[0] !== "/" ?
renderer.link(href.replace(/\.md/, ".html"), title, text) : renderer.link(href.replace(/\.md/, ".html"), title, text) :
renderer.link.apply(renderer, arguments); renderer.link.apply(renderer, arguments);
}; };
return customRenderer; return customRenderer;
} }

View File

@ -22,7 +22,8 @@
"split": "^1.0.0", "split": "^1.0.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"nomnoml": "^0.0.3", "nomnoml": "^0.0.3",
"canvas": "^1.2.7" "canvas": "^1.2.7",
"markdown-toc": "^0.11.7"
}, },
"scripts": { "scripts": {
"start": "node app.js", "start": "node app.js",

View File

@ -1,4 +1,9 @@
{ {
"configuration": {
"paths": {
"uuid": "uuid"
}
},
"extensions": { "extensions": {
"routes": [ "routes": [
{ {

View File

@ -25,7 +25,7 @@
* Module defining CreateService. Created by vwoeltje on 11/10/14. * Module defining CreateService. Created by vwoeltje on 11/10/14.
*/ */
define( define(
["../../lib/uuid"], ["uuid"],
function (uuid) { function (uuid) {
"use strict"; "use strict";

View File

@ -19,84 +19,90 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<!-- MINE -->
<div ng-controller="TimeRangeController"> <div ng-controller="TimeRangeController">
<div class="l-time-range-inputs-holder"> <div class="l-time-range-inputs-holder">
<span class="l-time-range-inputs-elem ui-symbol type-icon">&#x43;</span> <span class="l-time-range-inputs-elem ui-symbol type-icon">&#x43;</span>
<span class="l-time-range-input" ng-controller="ToggleController as t1"> <span class="l-time-range-input" ng-controller="ToggleController as t1">
<!--<span class="lbl">Start</span>--> <!--<span class="lbl">Start</span>-->
<span class="s-btn time-range-start" ng-click="t1.toggle()"> <span class="s-btn time-range-start">
<span class="val">{{startOuterText}}</span> <input type="text"
<a class="ui-symbol icon icon-calendar"></a> ng-model="boundsModel.start"
<mct-popup ng-if="t1.isActive()"> ng-class="{ error: !boundsModel.startValid }">
<div mct-click-elsewhere="t1.setState(false)"> </input>
<mct-control key="'datetime-picker'" <a class="ui-symbol icon icon-calendar" ng-click="t1.toggle()"></a>
ng-model="ngModel.outer" <mct-popup ng-if="t1.isActive()">
field="'start'" <div mct-click-elsewhere="t1.setState(false)">
options="{ hours: true }"> <mct-control key="'datetime-picker'"
</mct-control> ng-model="ngModel.outer"
</div> field="'start'"
</mct-popup> options="{ hours: true }">
</span> </mct-control>
</span> </div>
</mct-popup>
</span>
</span>
<span class="l-time-range-inputs-elem lbl">to</span> <span class="l-time-range-inputs-elem lbl">to</span>
<span class="l-time-range-input" ng-controller="ToggleController as t2"> <span class="l-time-range-input" ng-controller="ToggleController as t2">
<!--<span class="lbl">End</span>--> <!--<span class="lbl">End</span>-->
<span class="s-btn l-time-range-input" ng-click="t2.toggle()"> <span class="s-btn l-time-range-input">
<span class="val">{{endOuterText}}</span> <input type="text"
<a class="ui-symbol icon icon-calendar"></a> ng-model="boundsModel.end"
<mct-popup ng-if="t2.isActive()"> ng-class="{ error: !boundsModel.endValid }">
<div mct-click-elsewhere="t2.setState(false)"> </input>
<mct-control key="'datetime-picker'" <a class="ui-symbol icon icon-calendar" ng-click="t2.toggle()">
ng-model="ngModel.outer" </a>
field="'end'" <mct-popup ng-if="t2.isActive()">
options="{ hours: true }"> <div mct-click-elsewhere="t2.setState(false)">
</mct-control> <mct-control key="'datetime-picker'"
</div> ng-model="ngModel.outer"
</mct-popup> field="'end'"
</span>&nbsp; options="{ hours: true }">
</span> </mct-control>
</div> </div>
</mct-popup>
</span>&nbsp;
</span>
</div>
<div class="l-time-range-slider-holder"> <div class="l-time-range-slider-holder">
<div class="l-time-range-slider"> <div class="l-time-range-slider">
<div class="slider" <div class="slider"
mct-resize="spanWidth = bounds.width"> mct-resize="spanWidth = bounds.width">
<div class="knob knob-l" <div class="knob knob-l"
mct-drag-down="startLeftDrag()" mct-drag-down="startLeftDrag()"
mct-drag="leftDrag(delta[0])" mct-drag="leftDrag(delta[0])"
ng-style="{ left: startInnerPct }"> ng-style="{ left: startInnerPct }">
<div class="range-value">{{startInnerText}}</div> <div class="range-value">{{startInnerText}}</div>
</div> </div>
<div class="knob knob-r" <div class="knob knob-r"
mct-drag-down="startRightDrag()" mct-drag-down="startRightDrag()"
mct-drag="rightDrag(delta[0])" mct-drag="rightDrag(delta[0])"
ng-style="{ right: endInnerPct }"> ng-style="{ right: endInnerPct }">
<div class="range-value">{{endInnerText}}</div> <div class="range-value">{{endInnerText}}</div>
</div> </div>
<div class="slot range-holder"> <div class="slot range-holder">
<div class="range" <div class="range"
mct-drag-down="startMiddleDrag()" mct-drag-down="startMiddleDrag()"
mct-drag="middleDrag(delta[0])" mct-drag="middleDrag(delta[0])"
ng-style="{ left: startInnerPct, right: endInnerPct}"> ng-style="{ left: startInnerPct, right: endInnerPct}">
<div class="toi-line"></div> <div class="toi-line"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="l-time-range-ticks-holder"> <div class="l-time-range-ticks-holder">
<div class="l-time-range-ticks"> <div class="l-time-range-ticks">
<div <div
ng-repeat="tick in ticks" ng-repeat="tick in ticks"
ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }" ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
class="tick tick-x" class="tick tick-x"
> >
<span class="l-time-range-tick-label">{{tick}}</span> <span class="l-time-range-tick-label">{{tick}}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -26,9 +26,8 @@ define(
function (moment) { function (moment) {
"use strict"; "use strict";
var var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", TICK_SPACING_PX = 150;
TICK_SPACING_PX = 150;
/** /**
* @memberof platform/commonUI/general * @memberof platform/commonUI/general
@ -44,6 +43,15 @@ define(
return moment.utc(ts).format(DATE_FORMAT); return moment.utc(ts).format(DATE_FORMAT);
} }
function parseTimestamp(text) {
var m = moment.utc(text, DATE_FORMAT);
if (m.isValid()) {
return m.valueOf();
} else {
throw new Error("Could not parse " + text);
}
}
// From 0.0-1.0 to "0%"-"1%" // From 0.0-1.0 to "0%"-"1%"
function toPercent(p) { function toPercent(p) {
return (100 * p) + "%"; return (100 * p) + "%";
@ -93,6 +101,25 @@ define(
return { start: bounds.start, end: bounds.end }; return { start: bounds.start, end: bounds.end };
} }
function updateBoundsTextForProperty(ngModel, property) {
try {
if (!$scope.boundsModel[property] ||
parseTimestamp($scope.boundsModel[property]) !==
ngModel.outer[property]) {
$scope.boundsModel[property] =
formatTimestamp(ngModel.outer[property]);
}
} catch (e) {
// User-entered text is invalid, so leave it be
// until they fix it.
}
}
function updateBoundsText(ngModel) {
updateBoundsTextForProperty(ngModel, 'start');
updateBoundsTextForProperty(ngModel, 'end');
}
function updateViewFromModel(ngModel) { function updateViewFromModel(ngModel) {
var t = now(); var t = now();
@ -101,8 +128,7 @@ define(
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
// First, dates for the date pickers for outer bounds // First, dates for the date pickers for outer bounds
$scope.startOuterDate = new Date(ngModel.outer.start); updateBoundsText(ngModel);
$scope.endOuterDate = new Date(ngModel.outer.end);
// Then various updates for the inner span // Then various updates for the inner span
updateViewForInnerSpanFromModel(ngModel); updateViewForInnerSpanFromModel(ngModel);
@ -178,6 +204,8 @@ define(
function updateOuterStart(t) { function updateOuterStart(t) {
var ngModel = $scope.ngModel; var ngModel = $scope.ngModel;
ngModel.outer.start = t;
ngModel.outer.end = Math.max( ngModel.outer.end = Math.max(
ngModel.outer.start + outerMinimumSpan, ngModel.outer.start + outerMinimumSpan,
ngModel.outer.end ngModel.outer.end
@ -190,14 +218,15 @@ define(
ngModel.inner.end ngModel.inner.end
); );
$scope.startOuterText = formatTimestamp(t);
updateViewForInnerSpanFromModel(ngModel); updateViewForInnerSpanFromModel(ngModel);
updateTicks();
} }
function updateOuterEnd(t) { function updateOuterEnd(t) {
var ngModel = $scope.ngModel; var ngModel = $scope.ngModel;
ngModel.outer.end = t;
ngModel.outer.start = Math.min( ngModel.outer.start = Math.min(
ngModel.outer.end - outerMinimumSpan, ngModel.outer.end - outerMinimumSpan,
ngModel.outer.start ngModel.outer.start
@ -210,9 +239,40 @@ define(
ngModel.inner.start ngModel.inner.start
); );
$scope.endOuterText = formatTimestamp(t);
updateViewForInnerSpanFromModel(ngModel); updateViewForInnerSpanFromModel(ngModel);
updateTicks();
}
function updateStartFromText(value) {
try {
updateOuterStart(parseTimestamp(value));
updateBoundsTextForProperty($scope.ngModel, 'end');
$scope.boundsModel.startValid = true;
} catch (e) {
$scope.boundsModel.startValid = false;
return;
}
}
function updateEndFromText(value) {
try {
updateOuterEnd(parseTimestamp(value));
updateBoundsTextForProperty($scope.ngModel, 'start');
$scope.boundsModel.endValid = true;
} catch (e) {
$scope.boundsModel.endValid = false;
return;
}
}
function updateStartFromPicker(value) {
updateOuterStart(value);
updateBoundsText($scope.ngModel);
}
function updateEndFromPicker(value) {
updateOuterEnd(value);
updateBoundsText($scope.ngModel);
} }
$scope.startLeftDrag = startLeftDrag; $scope.startLeftDrag = startLeftDrag;
@ -224,14 +284,17 @@ define(
$scope.state = false; $scope.state = false;
$scope.ticks = []; $scope.ticks = [];
$scope.boundsModel = {};
// Initialize scope to defaults // Initialize scope to defaults
updateViewFromModel($scope.ngModel); updateViewFromModel($scope.ngModel);
$scope.$watchCollection("ngModel", updateViewFromModel); $scope.$watchCollection("ngModel", updateViewFromModel);
$scope.$watch("spanWidth", updateSpanWidth); $scope.$watch("spanWidth", updateSpanWidth);
$scope.$watch("ngModel.outer.start", updateOuterStart); $scope.$watch("ngModel.outer.start", updateStartFromPicker);
$scope.$watch("ngModel.outer.end", updateOuterEnd); $scope.$watch("ngModel.outer.end", updateEndFromPicker);
$scope.$watch("boundsModel.start", updateStartFromText);
$scope.$watch("boundsModel.end", updateEndFromText);
} }
return TimeConductorController; return TimeConductorController;

View File

@ -22,8 +22,8 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define( define(
["../../src/controllers/TimeRangeController"], ["../../src/controllers/TimeRangeController", "moment"],
function (TimeRangeController) { function (TimeRangeController, moment) {
"use strict"; "use strict";
var SEC = 1000, var SEC = 1000,
@ -166,8 +166,72 @@ define(
expect(mockScope.ngModel.inner.end) expect(mockScope.ngModel.inner.end)
.toBeGreaterThan(mockScope.ngModel.inner.start); .toBeGreaterThan(mockScope.ngModel.inner.start);
}); });
describe("by typing", function () {
it("updates models", function () {
var newStart = "1977-05-25 17:30:00",
newEnd = "2015-12-18 03:30:00";
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.ngModel.outer.start)
.toEqual(moment.utc(newStart).valueOf());
expect(mockScope.boundsModel.startValid)
.toBeTruthy();
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.ngModel.outer.end)
.toEqual(moment.utc(newEnd).valueOf());
expect(mockScope.boundsModel.endValid)
.toBeTruthy();
});
it("displays error state", function () {
var newStart = "Not a date",
newEnd = "Definitely not a date",
oldStart = mockScope.ngModel.outer.start,
oldEnd = mockScope.ngModel.outer.end;
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.ngModel.outer.start)
.toEqual(oldStart);
expect(mockScope.boundsModel.startValid)
.toBeFalsy();
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.ngModel.outer.end)
.toEqual(oldEnd);
expect(mockScope.boundsModel.endValid)
.toBeFalsy();
});
it("does not modify user input", function () {
// Don't want the controller "fixing" bad or
// irregularly-formatted input out from under
// the user's fingertips.
var newStart = "Not a date",
newEnd = "2015-3-3 01:02:04",
oldStart = mockScope.ngModel.outer.start,
oldEnd = mockScope.ngModel.outer.end;
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.boundsModel.start)
.toEqual(newStart);
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.boundsModel.end)
.toEqual(newEnd);
});
});
}); });
}); });
} }
); );

View File

@ -30,6 +30,14 @@
"category": "contextual", "category": "contextual",
"implementation": "actions/LinkAction.js", "implementation": "actions/LinkAction.js",
"depends": ["locationService", "linkService"] "depends": ["locationService", "linkService"]
},
{
"key": "follow",
"name": "Go To Original",
"description": "Go to the original, un-linked instance of this object.",
"glyph": "\u00F4",
"category": "contextual",
"implementation": "actions/GoToOriginalAction.js"
} }
], ],
"components": [ "components": [
@ -52,7 +60,8 @@
"key": "location", "key": "location",
"name": "Location Capability", "name": "Location Capability",
"description": "Provides a capability for retrieving the location of an object based upon it's context.", "description": "Provides a capability for retrieving the location of an object based upon it's context.",
"implementation": "capabilities/LocationCapability" "implementation": "capabilities/LocationCapability",
"depends": [ "$q", "$injector" ]
} }
], ],
"services": [ "services": [

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* 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";
/**
* Implements the "Go To Original" action, which follows a link back
* to an original instance of an object.
*
* @implements {Action}
* @constructor
* @private
* @memberof platform/entanglement
* @param {ActionContext} context the context in which the action
* will be performed
*/
function GoToOriginalAction(context) {
this.domainObject = context.domainObject;
}
GoToOriginalAction.prototype.perform = function () {
return this.domainObject.getCapability("location").getOriginal()
.then(function (originalObject) {
var actionCapability =
originalObject.getCapability("action");
return actionCapability &&
actionCapability.perform("navigate");
});
};
GoToOriginalAction.appliesTo = function (context) {
var domainObject = context.domainObject;
return domainObject && domainObject.hasCapability("location")
&& domainObject.getCapability("location").isLink();
};
return GoToOriginalAction;
}
);

View File

@ -1,3 +1,25 @@
/*****************************************************************************
* 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 */ /*global define */
define( define(
@ -12,11 +34,41 @@ define(
* *
* @constructor * @constructor
*/ */
function LocationCapability(domainObject) { function LocationCapability($q, $injector, domainObject) {
this.domainObject = domainObject; this.domainObject = domainObject;
this.$q = $q;
this.$injector = $injector;
return this; return this;
} }
/**
* Get an instance of this domain object in its original location.
*
* @returns {Promise.<DomainObject>} a promise for the original
* instance of this domain object
*/
LocationCapability.prototype.getOriginal = function () {
var id;
if (this.isOriginal()) {
return this.$q.when(this.domainObject);
}
id = this.domainObject.getId();
this.objectService =
this.objectService || this.$injector.get("objectService");
// Assume that an object will be correctly contextualized when
// loaded directly from the object service; this is true
// so long as LocatingObjectDecorator is present, and that
// decorator is also contained in this bundle.
return this.objectService.getObjects([id])
.then(function (objects) {
return objects[id];
});
};
/** /**
* Set the primary location (the parent id) of the current domain * Set the primary location (the parent id) of the current domain
* object. * object.
@ -78,10 +130,6 @@ define(
return !this.isLink(); return !this.isLink();
}; };
function createLocationCapability(domainObject) { return LocationCapability;
return new LocationCapability(domainObject);
}
return createLocationCapability;
} }
); );

View File

@ -0,0 +1,95 @@
/*****************************************************************************
* 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,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/actions/GoToOriginalAction',
'../DomainObjectFactory',
'../ControlledPromise'
],
function (GoToOriginalAction, domainObjectFactory, ControlledPromise) {
'use strict';
describe("The 'go to original' action", function () {
var testContext,
originalDomainObject,
mockLocationCapability,
mockOriginalActionCapability,
originalPromise,
action;
beforeEach(function () {
mockLocationCapability = jasmine.createSpyObj(
'location',
[ 'isLink', 'isOriginal', 'getOriginal' ]
);
mockOriginalActionCapability = jasmine.createSpyObj(
'action',
[ 'perform', 'getActions' ]
);
originalPromise = new ControlledPromise();
mockLocationCapability.getOriginal.andReturn(originalPromise);
mockLocationCapability.isLink.andReturn(true);
mockLocationCapability.isOriginal.andCallFake(function () {
return !mockLocationCapability.isLink();
});
testContext = {
domainObject: domainObjectFactory({
capabilities: {
location: mockLocationCapability
}
})
};
originalDomainObject = domainObjectFactory({
capabilities: {
action: mockOriginalActionCapability
}
});
action = new GoToOriginalAction(testContext);
});
it("is applicable to links", function () {
expect(GoToOriginalAction.appliesTo(testContext))
.toBeTruthy();
});
it("is not applicable to originals", function () {
mockLocationCapability.isLink.andReturn(false);
expect(GoToOriginalAction.appliesTo(testContext))
.toBeFalsy();
});
it("navigates to original objects when performed", function () {
expect(mockOriginalActionCapability.perform)
.not.toHaveBeenCalled();
action.perform();
originalPromise.resolve(originalDomainObject);
expect(mockOriginalActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
}
);

View File

@ -1,3 +1,25 @@
/*****************************************************************************
* 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,describe,it,expect,beforeEach,jasmine */ /*global define,describe,it,expect,beforeEach,jasmine */
define( define(
@ -7,6 +29,7 @@ define(
'../ControlledPromise' '../ControlledPromise'
], ],
function (LocationCapability, domainObjectFactory, ControlledPromise) { function (LocationCapability, domainObjectFactory, ControlledPromise) {
'use strict';
describe("LocationCapability", function () { describe("LocationCapability", function () {
@ -14,13 +37,17 @@ define(
var locationCapability, var locationCapability,
persistencePromise, persistencePromise,
mutationPromise, mutationPromise,
mockQ,
mockInjector,
mockObjectService,
domainObject; domainObject;
beforeEach(function () { beforeEach(function () {
domainObject = domainObjectFactory({ domainObject = domainObjectFactory({
id: "testObject",
capabilities: { capabilities: {
context: { context: {
getParent: function() { getParent: function () {
return domainObjectFactory({id: 'root'}); return domainObjectFactory({id: 'root'});
} }
}, },
@ -35,6 +62,11 @@ define(
} }
}); });
mockQ = jasmine.createSpyObj("$q", ["when"]);
mockInjector = jasmine.createSpyObj("$injector", ["get"]);
mockObjectService =
jasmine.createSpyObj("objectService", ["getObjects"]);
persistencePromise = new ControlledPromise(); persistencePromise = new ControlledPromise();
domainObject.capabilities.persistence.persist.andReturn( domainObject.capabilities.persistence.persist.andReturn(
persistencePromise persistencePromise
@ -49,7 +81,11 @@ define(
} }
); );
locationCapability = new LocationCapability(domainObject); locationCapability = new LocationCapability(
mockQ,
mockInjector,
domainObject
);
}); });
it("returns contextual location", function () { it("returns contextual location", function () {
@ -88,6 +124,57 @@ define(
expect(whenComplete).toHaveBeenCalled(); expect(whenComplete).toHaveBeenCalled();
}); });
describe("when used to load an original instance", function () {
var objectPromise,
qPromise,
originalObjects,
mockCallback;
function resolvePromises() {
if (mockQ.when.calls.length > 0) {
qPromise.resolve(mockQ.when.mostRecentCall.args[0]);
}
if (mockObjectService.getObjects.calls.length > 0) {
objectPromise.resolve(originalObjects);
}
}
beforeEach(function () {
objectPromise = new ControlledPromise();
qPromise = new ControlledPromise();
originalObjects = {
testObject: domainObjectFactory()
};
mockInjector.get.andCallFake(function (key) {
return key === 'objectService' && mockObjectService;
});
mockObjectService.getObjects.andReturn(objectPromise);
mockQ.when.andReturn(qPromise);
mockCallback = jasmine.createSpy('callback');
});
it("provides originals directly", function () {
domainObject.model.location = 'root';
locationCapability.getOriginal().then(mockCallback);
expect(mockCallback).not.toHaveBeenCalled();
resolvePromises();
expect(mockCallback)
.toHaveBeenCalledWith(domainObject);
});
it("loads from the object service for links", function () {
domainObject.model.location = 'some-other-root';
locationCapability.getOriginal().then(mockCallback);
expect(mockCallback).not.toHaveBeenCalled();
resolvePromises();
expect(mockCallback)
.toHaveBeenCalledWith(originalObjects.testObject);
});
});
}); });
}); });
} }

View File

@ -1,5 +1,9 @@
[ [
"actions/AbstractComposeAction", "actions/AbstractComposeAction",
"actions/CopyAction",
"actions/GoToOriginalAction",
"actions/LinkAction",
"actions/MoveAction",
"services/CopyService", "services/CopyService",
"services/LinkService", "services/LinkService",
"services/MoveService", "services/MoveService",

View File

@ -122,7 +122,7 @@ define([
mutationTopic.listen(function (mutatedObject) { mutationTopic.listen(function (mutatedObject) {
var id = mutatedObject.getId(); var id = mutatedObject.getId();
provider.indexed[id] = false; provider.indexedIds[id] = false;
provider.scheduleForIndexing(id); provider.scheduleForIndexing(id);
}); });
}; };

View File

@ -99,6 +99,14 @@ define([
.toHaveBeenCalledWith(jasmine.any(Function)); .toHaveBeenCalledWith(jasmine.any(Function));
}); });
it('reschedules indexing when mutation occurs', function () {
var mockDomainObject =
jasmine.createSpyObj('domainObj', ['getId']);
mockDomainObject.getId.andReturn("some-id");
mutationTopic.listen.mostRecentCall.args[0](mockDomainObject);
expect(provider.scheduleForIndexing).toHaveBeenCalledWith('some-id');
});
it('starts indexing roots', function () { it('starts indexing roots', function () {
expect(provider.scheduleForIndexing).toHaveBeenCalledWith('mine'); expect(provider.scheduleForIndexing).toHaveBeenCalledWith('mine');
}); });

View File

@ -45,7 +45,8 @@ require.config({
paths: { paths: {
'es6-promise': 'platform/framework/lib/es6-promise-2.0.0.min', 'es6-promise': 'platform/framework/lib/es6-promise-2.0.0.min',
'moment': 'platform/telemetry/lib/moment.min', 'moment': 'platform/telemetry/lib/moment.min',
'moment-duration-format': 'platform/features/clock/lib/moment-duration-format' 'moment-duration-format': 'platform/features/clock/lib/moment-duration-format',
'uuid': 'platform/commonUI/browse/lib/uuid'
}, },
shim: { shim: {