mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 05:07:52 +00:00
[Clocks/Timers] Bring in latest
Merge in latest from master branch to support review of changes, nasa/openmctweb#207
This commit is contained in:
commit
20cb2ff239
2
Procfile
2
Procfile
@ -1 +1 @@
|
|||||||
web: node app.js --port $PORT --include example/localstorage
|
web: node app.js --port $PORT
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"configuration": {
|
||||||
|
"paths": {
|
||||||
|
"uuid": "uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
@ -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">C</span>
|
<span class="l-time-range-inputs-elem ui-symbol type-icon">C</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>
|
options="{ hours: true }">
|
||||||
</span>
|
</mct-control>
|
||||||
</div>
|
</div>
|
||||||
|
</mct-popup>
|
||||||
|
</span>
|
||||||
|
</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>
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -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": [
|
||||||
|
62
platform/entanglement/src/actions/GoToOriginalAction.js
Normal file
62
platform/entanglement/src/actions/GoToOriginalAction.js
Normal 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;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
95
platform/entanglement/test/actions/GoToOriginalActionSpec.js
Normal file
95
platform/entanglement/test/actions/GoToOriginalActionSpec.js
Normal 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');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -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');
|
||||||
});
|
});
|
||||||
|
@ -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: {
|
||||||
|
Loading…
Reference in New Issue
Block a user