mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
[Timer] Re-implement Timer object in Vue.js (#4311)
* Re-implemented timer and clock and installed as a plugin * Clock indicator is installed as configuration to clock plugin * Uses moment Co-authored-by: Andrew Henry <akhenry@gmail.com> Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
This commit is contained in:
parent
b8fabb7e73
commit
6d4a324fca
@ -196,6 +196,7 @@
|
||||
{indicator: true}
|
||||
));
|
||||
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
|
||||
openmct.install(openmct.plugins.Timer());
|
||||
openmct.start();
|
||||
</script>
|
||||
</html>
|
||||
|
@ -1,209 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
"./src/actions/PauseTimerAction",
|
||||
"./res/templates/timer.html"
|
||||
], function (
|
||||
TickerService,
|
||||
TimerService,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
PauseTimerAction,
|
||||
timerTemplate
|
||||
) {
|
||||
return {
|
||||
name: "platform/features/clock",
|
||||
definition: {
|
||||
"name": "Clocks/Timers",
|
||||
"descriptions": "Domain objects for displaying current & relative times.",
|
||||
"configuration": {
|
||||
"paths": {
|
||||
"moment-duration-format": "moment-duration-format"
|
||||
},
|
||||
"shim": {
|
||||
"moment-duration-format": {
|
||||
"deps": [
|
||||
"moment"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"constants": [
|
||||
{
|
||||
"key": "CLOCK_INDICATOR_FORMAT",
|
||||
"value": "YYYY/MM/DD HH:mm:ss"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "tickerService",
|
||||
"implementation": TickerService,
|
||||
"depends": [
|
||||
"$timeout",
|
||||
"now"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "timerService",
|
||||
"implementation": TimerService,
|
||||
"depends": ["openmct"]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "TimerController",
|
||||
"implementation": TimerController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$window",
|
||||
"now"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "RefreshingController",
|
||||
"implementation": RefreshingController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"tickerService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [
|
||||
{
|
||||
"key": "timer",
|
||||
"type": "timer",
|
||||
"editable": false,
|
||||
"template": timerTemplate
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Start",
|
||||
"cssClass": "icon-play",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"key": "timer.pause",
|
||||
"implementation": PauseTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Pause",
|
||||
"cssClass": "icon-pause",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"key": "timer.restart",
|
||||
"implementation": RestartTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Restart at 0",
|
||||
"cssClass": "icon-refresh",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"key": "timer.stop",
|
||||
"implementation": StopTimerAction,
|
||||
"depends": [
|
||||
"now"
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Stop",
|
||||
"cssClass": "icon-box-round-corners",
|
||||
"priority": "preferred"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"key": "timer",
|
||||
"name": "Timer",
|
||||
"cssClass": "icon-timer",
|
||||
"description": "A timer that counts up or down to a datetime. Timers can be started, stopped and reset whenever needed, and support a variety of display formats. Each Timer displays the same value to all users. Timers can be added to Display Layouts.",
|
||||
"priority": 100,
|
||||
"features": [
|
||||
"creation"
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"key": "timestamp",
|
||||
"control": "datetime",
|
||||
"name": "Target"
|
||||
},
|
||||
{
|
||||
"key": "timerFormat",
|
||||
"control": "select",
|
||||
"name": "Display Format",
|
||||
"options": [
|
||||
{
|
||||
"value": "long",
|
||||
"name": "DDD hh:mm:ss"
|
||||
},
|
||||
{
|
||||
"value": "short",
|
||||
"name": "hh:mm:ss"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model": {
|
||||
"timerFormat": "DDD hh:mm:ss"
|
||||
}
|
||||
}
|
||||
],
|
||||
"runs": [],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "moment-duration-format",
|
||||
"version": "1.3.0",
|
||||
"author": "John Madhavan-Reese",
|
||||
"description": "Duration parsing/formatting",
|
||||
"website": "https://github.com/jsmreese/moment-duration-format",
|
||||
"copyright": "Copyright 2014 John Madhavan-Reese",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/jsmreese/moment-duration-format/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
@ -1,37 +0,0 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="c-timer u-style-receiver js-style-receiver is-{{timer.timerState}}" ng-controller="TimerController as timer">
|
||||
<div class="c-timer__controls">
|
||||
<button ng-click="timer.clickStopButton()"
|
||||
ng-hide="timer.timerState == 'stopped'"
|
||||
title="Reset"
|
||||
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button>
|
||||
<button ng-click="timer.clickButton()"
|
||||
title="{{timer.buttonText()}}"
|
||||
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button>
|
||||
</div>
|
||||
<div class="c-timer__direction {{timer.signClass()}}"
|
||||
ng-hide="!timer.signClass()"></div>
|
||||
<div class="c-timer__value">{{timer.text() || "--:--:--"}}
|
||||
</div>
|
||||
<span class="c-timer__ng-controller u-contents" ng-controller="RefreshingController"></span>
|
||||
</div>
|
@ -1,70 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Implements the "Pause" action for timers.
|
||||
*
|
||||
* Sets the reference pausedTime in a timer to the current
|
||||
* time, such that it stops counting up.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function PauseTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
PauseTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which have
|
||||
// a target time, or is in a playing state.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState === 'started';
|
||||
};
|
||||
|
||||
PauseTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now;
|
||||
|
||||
function updateModel(model) {
|
||||
model.timerState = 'paused';
|
||||
model.pausedTime = now();
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return PauseTimerAction;
|
||||
}
|
||||
);
|
@ -1,70 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Implements the "Restart at 0" action.
|
||||
*
|
||||
* Behaves the same as (and delegates functionality to)
|
||||
* the "Start" action.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function RestartTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
RestartTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which already have a target time.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState !== 'stopped';
|
||||
};
|
||||
|
||||
RestartTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now;
|
||||
|
||||
function updateModel(model) {
|
||||
model.timestamp = now();
|
||||
model.timerState = 'started';
|
||||
model.pausedTime = undefined;
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return RestartTimerAction;
|
||||
}
|
||||
);
|
@ -1,78 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Implements the "Start" action for timers.
|
||||
*
|
||||
* Sets the reference timestamp in a timer to the current
|
||||
* time, such that it begins counting up.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function StartTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
StartTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which do not yet have
|
||||
// a target time.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState !== 'started';
|
||||
};
|
||||
|
||||
StartTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now;
|
||||
|
||||
function updateModel(model) {
|
||||
//if we are resuming
|
||||
if (model.pausedTime) {
|
||||
var timeShift = now() - model.pausedTime;
|
||||
model.timestamp = model.timestamp + timeShift;
|
||||
} else {
|
||||
model.timestamp = now();
|
||||
}
|
||||
|
||||
model.timerState = 'started';
|
||||
model.pausedTime = undefined;
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return StartTimerAction;
|
||||
}
|
||||
);
|
@ -1,70 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Implements the "Stop" action for timers.
|
||||
*
|
||||
* Sets the reference timestamp in a timer undefined,
|
||||
* such that it is reset and makes no movements.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function StopTimerAction(now, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
StopTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
// We show this variant for timers which do not yet have
|
||||
// a target time.
|
||||
return model.type === 'timer'
|
||||
&& model.timerState !== 'stopped';
|
||||
};
|
||||
|
||||
StopTimerAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject;
|
||||
|
||||
function updateModel(model) {
|
||||
model.timestamp = undefined;
|
||||
model.timerState = 'stopped';
|
||||
model.pausedTime = undefined;
|
||||
}
|
||||
|
||||
return domainObject.useCapability('mutation', updateModel);
|
||||
};
|
||||
|
||||
return StopTimerAction;
|
||||
}
|
||||
);
|
@ -1,55 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Continually refreshes the represented domain object.
|
||||
*
|
||||
* This is a short-term workaround to assure Timer views stay
|
||||
* up-to-date; should be replaced by a global auto-refresh.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
* @param {angular.Scope} $scope the Angular scope
|
||||
* @param {platform/features/clock.TickerService} tickerService
|
||||
* a service used to align behavior with clock ticks
|
||||
*/
|
||||
function RefreshingController($scope, tickerService) {
|
||||
var unlisten;
|
||||
|
||||
function triggerRefresh() {
|
||||
var persistence = $scope.domainObject
|
||||
&& $scope.domainObject.getCapability('persistence');
|
||||
|
||||
return persistence && persistence.refresh();
|
||||
}
|
||||
|
||||
unlisten = tickerService.listen(triggerRefresh);
|
||||
$scope.$on('$destroy', unlisten);
|
||||
}
|
||||
|
||||
return RefreshingController;
|
||||
}
|
||||
);
|
@ -1,239 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./TimerFormatter'],
|
||||
function (TimerFormatter) {
|
||||
|
||||
var FORMATTER = new TimerFormatter();
|
||||
|
||||
/**
|
||||
* Controller for views of a Timer domain object.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
* @param {angular.Scope} $scope the Angular scope
|
||||
* @param $window Angular-provided window object
|
||||
* @param {Function} now a function which returns the current
|
||||
* time (typically wrapping `Date.now`)
|
||||
*/
|
||||
function TimerController($scope, $window, now) {
|
||||
var formatter,
|
||||
active = true,
|
||||
relativeTimestamp,
|
||||
lastTimestamp,
|
||||
relativeTimerState,
|
||||
self = this;
|
||||
|
||||
function update() {
|
||||
var timeDelta = lastTimestamp - relativeTimestamp;
|
||||
|
||||
if (formatter && !isNaN(timeDelta)) {
|
||||
self.textValue = formatter(timeDelta);
|
||||
self.signValue = timeDelta < 0 ? "-"
|
||||
: timeDelta >= 1000 ? "+" : "";
|
||||
self.signCssClass = timeDelta < 0 ? "icon-minus"
|
||||
: timeDelta >= 1000 ? "icon-plus" : "";
|
||||
} else {
|
||||
self.textValue = "";
|
||||
self.signValue = "";
|
||||
self.signCssClass = "";
|
||||
}
|
||||
}
|
||||
|
||||
function updateFormat(key) {
|
||||
formatter = FORMATTER[key] || FORMATTER.long;
|
||||
}
|
||||
|
||||
function updateTimestamp(timestamp) {
|
||||
relativeTimestamp = timestamp;
|
||||
}
|
||||
|
||||
function updateTimerState(timerState) {
|
||||
self.timerState = relativeTimerState = timerState;
|
||||
}
|
||||
|
||||
function updateActions(actionCapability, actionKey) {
|
||||
self.relevantAction = actionCapability
|
||||
&& actionCapability.getActions(actionKey)[0];
|
||||
|
||||
self.stopAction = relativeTimerState !== 'stopped'
|
||||
? actionCapability && actionCapability.getActions('timer.stop')[0] : undefined;
|
||||
|
||||
}
|
||||
|
||||
function isPaused() {
|
||||
return relativeTimerState === 'paused';
|
||||
}
|
||||
|
||||
function handleLegacyTimer(model) {
|
||||
if (model.timerState === undefined) {
|
||||
model.timerState = model.timestamp === undefined
|
||||
? 'stopped' : 'started';
|
||||
}
|
||||
}
|
||||
|
||||
function updateObject(domainObject) {
|
||||
var model = domainObject.getModel();
|
||||
handleLegacyTimer(model);
|
||||
|
||||
var timestamp = model.timestamp,
|
||||
formatKey = model.timerFormat,
|
||||
timerState = model.timerState,
|
||||
actionCapability = domainObject.getCapability('action'),
|
||||
actionKey = (timerState !== 'started')
|
||||
? 'timer.start' : 'timer.pause';
|
||||
|
||||
updateFormat(formatKey);
|
||||
updateTimestamp(timestamp);
|
||||
updateTimerState(timerState);
|
||||
updateActions(actionCapability, actionKey);
|
||||
|
||||
//if paused on startup show last known position
|
||||
if (isPaused() && !lastTimestamp) {
|
||||
lastTimestamp = model.pausedTime;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
function handleObjectChange(domainObject) {
|
||||
if (domainObject) {
|
||||
updateObject(domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
function handleModification() {
|
||||
handleObjectChange($scope.domainObject);
|
||||
}
|
||||
|
||||
function tick() {
|
||||
var lastSign = self.signValue,
|
||||
lastText = self.textValue;
|
||||
|
||||
if (!isPaused()) {
|
||||
lastTimestamp = now();
|
||||
update();
|
||||
}
|
||||
|
||||
if (relativeTimerState === undefined) {
|
||||
handleModification();
|
||||
}
|
||||
|
||||
// We're running in an animation frame, not in a digest cycle.
|
||||
// We need to trigger a digest cycle if our displayable data
|
||||
// changes.
|
||||
if (lastSign !== self.signValue || lastText !== self.textValue) {
|
||||
$scope.$apply();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
$window.requestAnimationFrame(tick);
|
||||
}
|
||||
}
|
||||
|
||||
$window.requestAnimationFrame(tick);
|
||||
|
||||
// Pull in the timer format from the domain object model
|
||||
$scope.$watch('domainObject', handleObjectChange);
|
||||
$scope.$watch('model.modified', handleModification);
|
||||
|
||||
// When the scope is destroyed, stop requesting anim. frames
|
||||
$scope.$on('$destroy', function () {
|
||||
active = false;
|
||||
});
|
||||
|
||||
this.$scope = $scope;
|
||||
this.signValue = '';
|
||||
this.textValue = '';
|
||||
this.updateObject = updateObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS class to display the right icon
|
||||
* for the start/pause button.
|
||||
* @returns {string} cssclass to display
|
||||
*/
|
||||
TimerController.prototype.buttonCssClass = function () {
|
||||
return this.relevantAction
|
||||
? this.relevantAction.getMetadata().cssClass : "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to show for the start/pause button
|
||||
* (e.g. in a tooltip)
|
||||
* @returns {string} name of the action
|
||||
*/
|
||||
TimerController.prototype.buttonText = function () {
|
||||
return this.relevantAction
|
||||
? this.relevantAction.getMetadata().name : "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform the action associated with the start/pause button.
|
||||
*/
|
||||
TimerController.prototype.clickButton = function () {
|
||||
if (this.relevantAction) {
|
||||
this.relevantAction.perform();
|
||||
this.updateObject(this.$scope.domainObject);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform the action associated with the stop button.
|
||||
*/
|
||||
TimerController.prototype.clickStopButton = function () {
|
||||
if (this.stopAction) {
|
||||
this.stopAction.perform();
|
||||
this.updateObject(this.$scope.domainObject);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the sign (+ or -) of the current timer value, as
|
||||
* displayable text.
|
||||
* @returns {string} sign of the current timer value
|
||||
*/
|
||||
TimerController.prototype.sign = function () {
|
||||
return this.signValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the sign (+ or -) of the current timer value, as
|
||||
* a CSS class.
|
||||
* @returns {string} sign of the current timer value
|
||||
*/
|
||||
TimerController.prototype.signClass = function () {
|
||||
return this.signCssClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to display for the current timer value.
|
||||
* @returns {string} current timer value
|
||||
*/
|
||||
TimerController.prototype.text = function () {
|
||||
return this.textValue;
|
||||
};
|
||||
|
||||
return TimerController;
|
||||
}
|
||||
);
|
@ -1,73 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['moment', 'moment-duration-format'],
|
||||
function (moment) {
|
||||
|
||||
var SHORT_FORMAT = "HH:mm:ss",
|
||||
LONG_FORMAT = "d[D] HH:mm:ss";
|
||||
|
||||
/**
|
||||
* Provides formatting functions for Timers.
|
||||
*
|
||||
* Display formats for timers are a little different from what
|
||||
* moment.js provides, so we have custom logic here. This specifically
|
||||
* supports `TimerController`.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
*/
|
||||
function TimerFormatter() {
|
||||
}
|
||||
|
||||
// Round this timestamp down to the second boundary
|
||||
// (e.g. 1124ms goes down to 1000ms, -2400ms goes down to -3000ms)
|
||||
function toWholeSeconds(duration) {
|
||||
return Math.abs(Math.floor(duration / 1000) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a duration for display, using the short form.
|
||||
* (e.g. 03:33:11)
|
||||
* @param {number} duration the duration, in milliseconds
|
||||
* @param {boolean} sign true if positive
|
||||
*/
|
||||
TimerFormatter.prototype.short = function (duration) {
|
||||
return moment.duration(toWholeSeconds(duration), 'ms')
|
||||
.format(SHORT_FORMAT, { trim: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a duration for display, using the long form.
|
||||
* (e.g. 0d 03:33:11)
|
||||
* @param {number} duration the duration, in milliseconds
|
||||
* @param {boolean} sign true if positive
|
||||
*/
|
||||
TimerFormatter.prototype.long = function (duration) {
|
||||
return moment.duration(toWholeSeconds(duration), 'ms')
|
||||
.format(LONG_FORMAT, { trim: false });
|
||||
};
|
||||
|
||||
return TimerFormatter;
|
||||
}
|
||||
);
|
@ -1,87 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Calls functions every second, as close to the actual second
|
||||
* tick as is feasible.
|
||||
* @constructor
|
||||
* @memberof platform/features/clock
|
||||
* @param $timeout Angular's $timeout
|
||||
* @param {Function} now function to provide the current time in ms
|
||||
*/
|
||||
function TickerService($timeout, now) {
|
||||
var self = this;
|
||||
|
||||
function tick() {
|
||||
var timestamp = now(),
|
||||
millis = timestamp % 1000;
|
||||
|
||||
// Only update callbacks if a second has actually passed.
|
||||
if (timestamp >= self.last + 1000) {
|
||||
self.callbacks.forEach(function (callback) {
|
||||
callback(timestamp);
|
||||
});
|
||||
self.last = timestamp - millis;
|
||||
}
|
||||
|
||||
// Try to update at exactly the next second
|
||||
$timeout(tick, 1000 - millis, true);
|
||||
}
|
||||
|
||||
tick();
|
||||
|
||||
this.callbacks = [];
|
||||
this.last = now() - 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for clock ticks. The provided callback will
|
||||
* be invoked with the current timestamp (in milliseconds
|
||||
* since Jan 1 1970) at regular intervals, as near to the
|
||||
* second boundary as possible.
|
||||
*
|
||||
* @param {Function} callback callback to invoke
|
||||
* @returns {Function} a function to unregister this listener
|
||||
*/
|
||||
TickerService.prototype.listen = function (callback) {
|
||||
var self = this;
|
||||
|
||||
self.callbacks.push(callback);
|
||||
|
||||
// Provide immediate feedback
|
||||
callback(this.last);
|
||||
|
||||
// Provide a deregistration function
|
||||
return function () {
|
||||
self.callbacks = self.callbacks.filter(function (cb) {
|
||||
return cb !== callback;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return TickerService;
|
||||
}
|
||||
);
|
@ -1,113 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['EventEmitter'], function (EventEmitter) {
|
||||
|
||||
/**
|
||||
* Tracks the currently-followed Timer object. Used by
|
||||
* timelines et al to synchronize to a particular timer.
|
||||
*
|
||||
* The TimerService emits `change` events when the active timer
|
||||
* is changed.
|
||||
*/
|
||||
function TimerService(openmct) {
|
||||
EventEmitter.apply(this);
|
||||
this.time = openmct.time;
|
||||
this.objects = openmct.objects;
|
||||
}
|
||||
|
||||
TimerService.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Set (or clear, if `timer` is undefined) the currently active timer.
|
||||
* @param {DomainObject} timer the new active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.setTimer = function (timer) {
|
||||
this.timer = timer;
|
||||
this.emit('change', timer);
|
||||
|
||||
if (this.stopObserving) {
|
||||
this.stopObserving();
|
||||
delete this.stopObserving;
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
this.stopObserving =
|
||||
this.objects.observe(timer, '*', this.setTimer.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently active timer.
|
||||
* @return {DomainObject} the active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.getTimer = function () {
|
||||
return this.timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is a currently active timer.
|
||||
* @return {boolean} true if there is a timer
|
||||
*/
|
||||
TimerService.prototype.hasTimer = function () {
|
||||
return Boolean(this.timer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the provided timestamp to milliseconds relative to
|
||||
* the active timer.
|
||||
* @return {number} milliseconds since timer start
|
||||
*/
|
||||
TimerService.prototype.convert = function (timestamp) {
|
||||
var clock = this.time.clock();
|
||||
var canConvert = this.hasTimer()
|
||||
&& Boolean(clock)
|
||||
&& this.timer.timerState !== 'stopped';
|
||||
|
||||
if (!canConvert) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var now = clock.currentValue();
|
||||
var delta = this.timer.timerState === 'paused'
|
||||
? now - this.timer.pausedTime : 0;
|
||||
var epoch = this.timer.timestamp;
|
||||
|
||||
return timestamp - epoch - delta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the active clock, adjusted to be relative to the active
|
||||
* timer. If there is no clock or no active timer, this will return
|
||||
* `undefined`.
|
||||
* @return {number} milliseconds since the start of the active timer
|
||||
*/
|
||||
TimerService.prototype.now = function () {
|
||||
var clock = this.time.clock();
|
||||
|
||||
return clock && this.convert(clock.currentValue());
|
||||
};
|
||||
|
||||
return TimerService;
|
||||
});
|
@ -1,106 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/actions/PauseTimerAction"],
|
||||
function (PauseTimerAction) {
|
||||
|
||||
describe("A timer's Pause action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(PauseTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = {domainObject: mockDomainObject};
|
||||
|
||||
action = new PauseTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = 'started';
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('paused');
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = undefined;
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(12000);
|
||||
});
|
||||
|
||||
it("applies only to timers in a playing state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, false);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, false);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, true);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'started', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,112 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/actions/RestartTimerAction"],
|
||||
function (RestartTimerAction) {
|
||||
|
||||
describe("A timer's restart action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = { domainObject: mockDomainObject };
|
||||
|
||||
action = new RestartTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timestamp", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.timestamp).toEqual(12000);
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = 'stopped';
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('started');
|
||||
});
|
||||
|
||||
it("applies only to timers in a non-stopped state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, false);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, true);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, true);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'paused', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,111 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/actions/StartTimerAction"],
|
||||
function (StartTimerAction) {
|
||||
|
||||
describe("A timer's start action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(StartTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = {domainObject: mockDomainObject};
|
||||
|
||||
action = new StartTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timestamp", function () {
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.timestamp).toEqual(12000);
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = undefined;
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('started');
|
||||
});
|
||||
|
||||
it("applies only to timers not in a playing state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, true);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, true);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, false);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'paused', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,111 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/actions/StopTimerAction"],
|
||||
function (StopTimerAction) {
|
||||
|
||||
describe("A timer's stop action", function () {
|
||||
var mockNow,
|
||||
mockDomainObject,
|
||||
testModel,
|
||||
testContext,
|
||||
action;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testState(type, timerState, timestamp, expected) {
|
||||
testModel.type = type;
|
||||
testModel.timerState = timerState;
|
||||
testModel.timestamp = timestamp;
|
||||
|
||||
if (expected) {
|
||||
expect(StopTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||
} else {
|
||||
expect(StopTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
|
||||
mockDomainObject.useCapability.and.callFake(function (c, v) {
|
||||
if (c === 'mutation') {
|
||||
testModel = v(testModel) || testModel;
|
||||
|
||||
return asPromise(true);
|
||||
}
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
|
||||
testModel = {};
|
||||
testContext = {domainObject: mockDomainObject};
|
||||
|
||||
action = new StopTimerAction(mockNow, testContext);
|
||||
});
|
||||
|
||||
it("updates the model with a timestamp", function () {
|
||||
mockNow.and.returnValue(12000);
|
||||
action.perform();
|
||||
expect(testModel.timestamp).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a pausedTime", function () {
|
||||
testModel.pausedTime = 12000;
|
||||
action.perform();
|
||||
expect(testModel.pausedTime).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("updates the model with a timerState", function () {
|
||||
testModel.timerState = 'started';
|
||||
action.perform();
|
||||
expect(testModel.timerState).toEqual('stopped');
|
||||
});
|
||||
|
||||
it("applies only to timers in a non-stopped state", function () {
|
||||
//in a stopped state
|
||||
testState('timer', 'stopped', undefined, false);
|
||||
|
||||
//in a paused state
|
||||
testState('timer', 'paused', 12000, true);
|
||||
|
||||
//in a playing state
|
||||
testState('timer', 'started', 12000, true);
|
||||
|
||||
//not a timer
|
||||
testState('clock', 'paused', 12000, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,80 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/controllers/RefreshingController"],
|
||||
function (RefreshingController) {
|
||||
|
||||
describe("The refreshing controller", function () {
|
||||
var mockScope,
|
||||
mockTicker,
|
||||
mockUnticker,
|
||||
controller;
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj('$scope', ['$on']);
|
||||
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
|
||||
mockUnticker = jasmine.createSpy('unticker');
|
||||
|
||||
mockTicker.listen.and.returnValue(mockUnticker);
|
||||
|
||||
controller = new RefreshingController(mockScope, mockTicker);
|
||||
});
|
||||
|
||||
it("refreshes the represented object on every tick", function () {
|
||||
var mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability']
|
||||
),
|
||||
mockPersistence = jasmine.createSpyObj(
|
||||
'persistence',
|
||||
['persist', 'refresh']
|
||||
);
|
||||
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
return (c === 'persistence') && mockPersistence;
|
||||
});
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockTicker.listen.calls.mostRecent().args[0](12321);
|
||||
expect(mockPersistence.refresh).toHaveBeenCalled();
|
||||
expect(mockPersistence.persist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("subscribes to clock ticks", function () {
|
||||
expect(mockTicker.listen)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("unsubscribes to ticks when destroyed", function () {
|
||||
// Make sure $destroy is being listened for...
|
||||
expect(mockScope.$on.calls.mostRecent().args[0]).toEqual('$destroy');
|
||||
expect(mockUnticker).not.toHaveBeenCalled();
|
||||
|
||||
// ...and makes sure that its listener unsubscribes from ticker
|
||||
mockScope.$on.calls.mostRecent().args[1]();
|
||||
expect(mockUnticker).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,227 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/controllers/TimerController"],
|
||||
function (TimerController) {
|
||||
|
||||
// Wed, 03 Jun 2015 17:56:14 GMT
|
||||
var TEST_TIMESTAMP = 1433354174000;
|
||||
|
||||
describe("A timer view's controller", function () {
|
||||
var mockScope,
|
||||
mockWindow,
|
||||
mockNow,
|
||||
mockDomainObject,
|
||||
mockActionCapability,
|
||||
mockStart,
|
||||
mockPause,
|
||||
mockStop,
|
||||
testModel,
|
||||
controller;
|
||||
|
||||
function invokeWatch(expr, value) {
|
||||
mockScope.$watch.calls.all().forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
'$scope',
|
||||
['$watch', '$on', '$apply']
|
||||
);
|
||||
mockWindow = jasmine.createSpyObj(
|
||||
'$window',
|
||||
['requestAnimationFrame']
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getCapability', 'useCapability', 'getModel']
|
||||
);
|
||||
mockActionCapability = jasmine.createSpyObj(
|
||||
'action',
|
||||
['getActions']
|
||||
);
|
||||
mockStart = jasmine.createSpyObj(
|
||||
'start',
|
||||
['getMetadata', 'perform']
|
||||
);
|
||||
mockPause = jasmine.createSpyObj(
|
||||
'paused',
|
||||
['getMetadata', 'perform']
|
||||
);
|
||||
mockStop = jasmine.createSpyObj(
|
||||
'stopped',
|
||||
['getMetadata', 'perform']
|
||||
);
|
||||
mockNow = jasmine.createSpy('now');
|
||||
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
return (c === 'action') && mockActionCapability;
|
||||
});
|
||||
mockDomainObject.getModel.and.callFake(function () {
|
||||
return testModel;
|
||||
});
|
||||
mockActionCapability.getActions.and.callFake(function (k) {
|
||||
return [{
|
||||
'timer.start': mockStart,
|
||||
'timer.pause': mockPause,
|
||||
'timer.stop': mockStop
|
||||
}[k]];
|
||||
});
|
||||
|
||||
mockStart.getMetadata.and.returnValue({
|
||||
cssClass: "icon-play",
|
||||
name: "Start"
|
||||
});
|
||||
mockPause.getMetadata.and.returnValue({
|
||||
cssClass: "icon-pause",
|
||||
name: "Pause"
|
||||
});
|
||||
mockStop.getMetadata.and.returnValue({
|
||||
cssClass: "icon-box-round-corners",
|
||||
name: "Stop"
|
||||
});
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
testModel = {};
|
||||
|
||||
controller = new TimerController(mockScope, mockWindow, mockNow);
|
||||
});
|
||||
|
||||
it("watches for the domain object in view", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"domainObject",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("watches for domain object modifications", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"model.modified",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("updates on a timer", function () {
|
||||
expect(mockWindow.requestAnimationFrame)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("displays nothing when there is no target", function () {
|
||||
// Notify that domain object is available via scope
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("");
|
||||
expect(controller.signClass()).toEqual("");
|
||||
expect(controller.text()).toEqual("");
|
||||
});
|
||||
|
||||
it("formats time to display relative to target", function () {
|
||||
testModel.timestamp = TEST_TIMESTAMP;
|
||||
testModel.timerFormat = 'long';
|
||||
// Notify that domain object is available via scope
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP + 121000);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("+");
|
||||
expect(controller.signClass()).toEqual("icon-plus");
|
||||
expect(controller.text()).toEqual("0D 00:02:01");
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP - 121000);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("-");
|
||||
expect(controller.signClass()).toEqual("icon-minus");
|
||||
expect(controller.text()).toEqual("0D 00:02:01");
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(controller.sign()).toEqual("");
|
||||
expect(controller.signClass()).toEqual("");
|
||||
expect(controller.text()).toEqual("0D 00:00:00");
|
||||
});
|
||||
|
||||
it("shows cssClass & name for the applicable start/pause action", function () {
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
expect(controller.buttonCssClass()).toEqual("icon-play");
|
||||
expect(controller.buttonText()).toEqual("Start");
|
||||
|
||||
testModel.timestamp = 12321;
|
||||
testModel.timerState = 'started';
|
||||
invokeWatch('model.modified', 1);
|
||||
expect(controller.buttonCssClass()).toEqual("icon-pause");
|
||||
expect(controller.buttonText()).toEqual("Pause");
|
||||
});
|
||||
|
||||
it("performs correct start/pause/stop action on click", function () {
|
||||
//test start
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
expect(mockStart.perform).not.toHaveBeenCalled();
|
||||
controller.clickButton();
|
||||
expect(mockStart.perform).toHaveBeenCalled();
|
||||
|
||||
//test pause
|
||||
testModel.timestamp = 12321;
|
||||
testModel.timerState = 'started';
|
||||
invokeWatch('model.modified', 1);
|
||||
expect(mockPause.perform).not.toHaveBeenCalled();
|
||||
controller.clickButton();
|
||||
expect(mockPause.perform).toHaveBeenCalled();
|
||||
|
||||
//test stop
|
||||
expect(mockStop.perform).not.toHaveBeenCalled();
|
||||
controller.clickStopButton();
|
||||
expect(mockStop.perform).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("stops requesting animation frames when destroyed", function () {
|
||||
var initialCount = mockWindow.requestAnimationFrame.calls.count();
|
||||
|
||||
// First, check that normally new frames keep getting requested
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 1);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 2);
|
||||
|
||||
// Now, verify that it stops after $destroy
|
||||
expect(mockScope.$on.calls.mostRecent().args[0])
|
||||
.toEqual('$destroy');
|
||||
mockScope.$on.calls.mostRecent().args[1]();
|
||||
|
||||
// Frames should no longer get requested
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 2);
|
||||
mockWindow.requestAnimationFrame.calls.mostRecent().args[0]();
|
||||
expect(mockWindow.requestAnimationFrame.calls.count())
|
||||
.toEqual(initialCount + 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,111 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/controllers/TimerFormatter"],
|
||||
function (TimerFormatter) {
|
||||
|
||||
var MS_IN_SEC = 1000,
|
||||
MS_IN_MIN = MS_IN_SEC * 60,
|
||||
MS_IN_HR = MS_IN_MIN * 60,
|
||||
MS_IN_DAY = MS_IN_HR * 24;
|
||||
|
||||
describe("The timer value formatter", function () {
|
||||
var formatter = new TimerFormatter();
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function toDuration(days, hours, mins, secs) {
|
||||
return [
|
||||
days * MS_IN_DAY,
|
||||
hours * MS_IN_HR,
|
||||
mins * MS_IN_MIN,
|
||||
secs * MS_IN_SEC
|
||||
].reduce(sum, 0);
|
||||
}
|
||||
|
||||
it("formats short-form values (no days)", function () {
|
||||
expect(formatter.short(toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("123:02:03");
|
||||
});
|
||||
|
||||
it("formats negative short-form values (no days)", function () {
|
||||
expect(formatter.short(-toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("123:02:03");
|
||||
});
|
||||
|
||||
it("formats long-form values (with days)", function () {
|
||||
expect(formatter.long(toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("5D 03:02:03");
|
||||
});
|
||||
|
||||
it("formats negative long-form values (no days)", function () {
|
||||
expect(formatter.long(-toDuration(0, 123, 2, 3) + 123))
|
||||
.toEqual("5D 03:02:03");
|
||||
});
|
||||
|
||||
it("rounds seconds down for positive durations", function () {
|
||||
expect(formatter.short(MS_IN_SEC + 600))
|
||||
.toEqual("00:00:01");
|
||||
});
|
||||
|
||||
it("rounds seconds up for negative durations", function () {
|
||||
expect(formatter.short(-MS_IN_SEC - 600))
|
||||
.toEqual("00:00:02");
|
||||
});
|
||||
|
||||
it("short-formats correctly around negative time borders", function () {
|
||||
expect(formatter.short(-1)).toEqual("00:00:01");
|
||||
expect(formatter.short(-1000)).toEqual("00:00:01");
|
||||
expect(formatter.short(-1001)).toEqual("00:00:02");
|
||||
expect(formatter.short(-2000)).toEqual("00:00:02");
|
||||
expect(formatter.short(-59001)).toEqual("00:01:00");
|
||||
expect(formatter.short(-60000)).toEqual("00:01:00");
|
||||
|
||||
expect(formatter.short(-MS_IN_HR + 999)).toEqual("01:00:00");
|
||||
expect(formatter.short(-MS_IN_HR)).toEqual("01:00:00");
|
||||
});
|
||||
|
||||
it("differentiates between values around zero", function () {
|
||||
// These are more than 1000 ms apart so should not appear
|
||||
// as the same second
|
||||
expect(formatter.short(-999))
|
||||
.not.toEqual(formatter.short(999));
|
||||
});
|
||||
|
||||
it("handles negative days", function () {
|
||||
expect(formatter.long(-10 * MS_IN_DAY))
|
||||
.toEqual("10D 00:00:00");
|
||||
expect(formatter.long(-10 * MS_IN_DAY + 100))
|
||||
.toEqual("10D 00:00:00");
|
||||
expect(formatter.long(-10 * MS_IN_DAY + 999))
|
||||
.toEqual("10D 00:00:00");
|
||||
|
||||
expect(formatter.short(-10 * MS_IN_DAY + 100))
|
||||
.toEqual("240:00:00");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,62 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/services/TickerService"],
|
||||
function (TickerService) {
|
||||
|
||||
var TEST_TIMESTAMP = 1433354174000;
|
||||
|
||||
describe("The ticker service", function () {
|
||||
var mockTimeout,
|
||||
mockNow,
|
||||
mockCallback,
|
||||
tickerService;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy('$timeout');
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockCallback = jasmine.createSpy('callback');
|
||||
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP);
|
||||
|
||||
tickerService = new TickerService(mockTimeout, mockNow);
|
||||
});
|
||||
|
||||
it("notifies listeners of clock ticks", function () {
|
||||
tickerService.listen(mockCallback);
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP + 12321);
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
expect(mockCallback)
|
||||
.toHaveBeenCalledWith(TEST_TIMESTAMP + 12321);
|
||||
});
|
||||
|
||||
it("allows listeners to unregister", function () {
|
||||
tickerService.listen(mockCallback)(); // Unregister immediately
|
||||
mockNow.and.returnValue(TEST_TIMESTAMP + 12321);
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
expect(mockCallback).not
|
||||
.toHaveBeenCalledWith(TEST_TIMESTAMP + 12321);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,77 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'../../src/services/TimerService'
|
||||
], function (TimerService) {
|
||||
describe("TimerService", function () {
|
||||
var callback;
|
||||
var mockmct;
|
||||
var timerService;
|
||||
|
||||
beforeEach(function () {
|
||||
callback = jasmine.createSpy('callback');
|
||||
mockmct = {
|
||||
time: { clock: jasmine.createSpy('clock') },
|
||||
objects: { observe: jasmine.createSpy('observe') }
|
||||
};
|
||||
timerService = new TimerService(mockmct);
|
||||
timerService.on('change', callback);
|
||||
});
|
||||
|
||||
it("initially emits no change events", function () {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports no current timer", function () {
|
||||
expect(timerService.getTimer()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("setTimer", function () {
|
||||
var testTimer;
|
||||
|
||||
beforeEach(function () {
|
||||
testTimer = { name: "I am some timer; you are nobody." };
|
||||
timerService.setTimer(testTimer);
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports the current timer", function () {
|
||||
expect(timerService.getTimer()).toBe(testTimer);
|
||||
});
|
||||
|
||||
it("observes changes to an object", function () {
|
||||
var newTimer = { name: "I am another timer." };
|
||||
expect(mockmct.objects.observe).toHaveBeenCalledWith(
|
||||
testTimer,
|
||||
'*',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
mockmct.objects.observe.calls.mostRecent().args[2](newTimer);
|
||||
expect(timerService.getTimer()).toBe(newTimer);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -473,6 +473,23 @@ ObjectAPI.prototype.mutate = function (domainObject, path, value) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates a domain object based on its latest persisted state. Note that this will mutate the provided object.
|
||||
* @param {module:openmct.DomainObject} domainObject an object to refresh from its persistence store
|
||||
* @returns {Promise} the provided object, updated to reflect the latest persisted state of the object.
|
||||
*/
|
||||
ObjectAPI.prototype.refresh = async function (domainObject) {
|
||||
const refreshedObject = await this.get(domainObject.identifier);
|
||||
|
||||
if (domainObject.isMutable) {
|
||||
domainObject.$refresh(refreshedObject);
|
||||
} else {
|
||||
utils.refresh(domainObject, refreshedObject);
|
||||
}
|
||||
|
||||
return domainObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -37,7 +37,6 @@ const DEFAULTS = [
|
||||
'platform/execution',
|
||||
'platform/exporters',
|
||||
'platform/telemetry',
|
||||
'platform/features/clock',
|
||||
'platform/forms',
|
||||
'platform/identity',
|
||||
'platform/persistence/aggregator',
|
||||
@ -77,7 +76,6 @@ define([
|
||||
'../platform/entanglement/bundle',
|
||||
'../platform/execution/bundle',
|
||||
'../platform/exporters/bundle',
|
||||
'../platform/features/clock/bundle',
|
||||
'../platform/features/static-markup/bundle',
|
||||
'../platform/forms/bundle',
|
||||
'../platform/framework/bundle',
|
||||
|
@ -41,6 +41,7 @@
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import momentTimezone from 'moment-timezone';
|
||||
import ticker from 'utils/clock/Ticker';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
@ -82,8 +83,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const TickerService = this.openmct.$injector.get('tickerService');
|
||||
this.unlisten = TickerService.listen(this.tick);
|
||||
this.unlisten = ticker.listen(this.tick);
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.unlisten) {
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import ticker from 'utils/clock/Ticker';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
@ -45,10 +46,7 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.on('start', () => {
|
||||
const TickerService = this.openmct.$injector.get('tickerService');
|
||||
this.unlisten = TickerService.listen(this.tick);
|
||||
});
|
||||
this.unlisten = ticker.listen(this.tick);
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.unlisten) {
|
||||
|
@ -72,6 +72,7 @@ define([
|
||||
'./timeline/plugin',
|
||||
'./hyperlink/plugin',
|
||||
'./clock/plugin',
|
||||
'./timer/plugin',
|
||||
'./DeviceClassifier/plugin'
|
||||
], function (
|
||||
_,
|
||||
@ -125,6 +126,7 @@ define([
|
||||
Timeline,
|
||||
Hyperlink,
|
||||
Clock,
|
||||
Timer,
|
||||
DeviceClassifier
|
||||
) {
|
||||
const bundleMap = {
|
||||
@ -232,6 +234,7 @@ define([
|
||||
plugins.Timeline = Timeline.default;
|
||||
plugins.Hyperlink = Hyperlink.default;
|
||||
plugins.Clock = Clock.default;
|
||||
plugins.Timer = Timer.default;
|
||||
plugins.DeviceClassifier = DeviceClassifier.default;
|
||||
|
||||
return plugins;
|
||||
|
65
src/plugins/timer/TimerViewProvider.js
Normal file
65
src/plugins/timer/TimerViewProvider.js
Normal file
@ -0,0 +1,65 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import Timer from './components/Timer.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default function TimerViewProvider(openmct) {
|
||||
return {
|
||||
key: 'timer.view',
|
||||
name: 'Timer',
|
||||
cssClass: 'icon-timer',
|
||||
canView(domainObject) {
|
||||
return domainObject.type === 'timer';
|
||||
},
|
||||
|
||||
view: function (domainObject, objectPath) {
|
||||
let component;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
Timer
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
objectPath,
|
||||
currentView: this
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject
|
||||
};
|
||||
},
|
||||
template: '<timer :domain-object="domainObject" />'
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
56
src/plugins/timer/actions/PauseTimerAction.js
Normal file
56
src/plugins/timer/actions/PauseTimerAction.js
Normal file
@ -0,0 +1,56 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class PauseTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Pause';
|
||||
this.key = 'timer.pause';
|
||||
this.description = 'Pause the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-pause';
|
||||
this.priority = 3;
|
||||
|
||||
this.openmct = openmct;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return new Error('Unable to run pause timer action. No domainObject provided.');
|
||||
}
|
||||
|
||||
const newConfiguration = { ...domainObject.configuration };
|
||||
newConfiguration.timerState = 'paused';
|
||||
newConfiguration.pausedTime = new Date();
|
||||
|
||||
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { timerState } = domainObject.configuration;
|
||||
|
||||
return domainObject.type === 'timer' && timerState === 'started';
|
||||
}
|
||||
}
|
57
src/plugins/timer/actions/RestartTimerAction.js
Normal file
57
src/plugins/timer/actions/RestartTimerAction.js
Normal file
@ -0,0 +1,57 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class RestartTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Restart at 0';
|
||||
this.key = 'timer.restart';
|
||||
this.description = 'Restart the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-refresh';
|
||||
this.priority = 2;
|
||||
|
||||
this.openmct = openmct;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return new Error('Unable to run restart timer action. No domainObject provided.');
|
||||
}
|
||||
|
||||
const newConfiguration = { ...domainObject.configuration };
|
||||
newConfiguration.timerState = 'started';
|
||||
newConfiguration.timestamp = new Date();
|
||||
newConfiguration.pausedTime = undefined;
|
||||
|
||||
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { timerState } = domainObject.configuration;
|
||||
|
||||
return domainObject.type === 'timer' && timerState !== 'stopped';
|
||||
}
|
||||
}
|
76
src/plugins/timer/actions/StartTimerAction.js
Normal file
76
src/plugins/timer/actions/StartTimerAction.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
export default class StartTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Start';
|
||||
this.key = 'timer.start';
|
||||
this.description = 'Start the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-play';
|
||||
this.priority = 3;
|
||||
|
||||
this.openmct = openmct;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return new Error('Unable to run start timer action. No domainObject provided.');
|
||||
}
|
||||
|
||||
let { pausedTime, timestamp } = domainObject.configuration;
|
||||
const newConfiguration = { ...domainObject.configuration };
|
||||
|
||||
if (pausedTime) {
|
||||
pausedTime = moment(pausedTime);
|
||||
}
|
||||
|
||||
if (timestamp) {
|
||||
timestamp = moment(timestamp);
|
||||
}
|
||||
|
||||
const now = moment(new Date());
|
||||
if (pausedTime) {
|
||||
const timeShift = moment.duration(now.diff(pausedTime));
|
||||
const shiftedTime = timestamp.add(timeShift);
|
||||
newConfiguration.timestamp = shiftedTime.toDate();
|
||||
} else if (!timestamp) {
|
||||
newConfiguration.timestamp = now.toDate();
|
||||
}
|
||||
|
||||
newConfiguration.timerState = 'started';
|
||||
newConfiguration.pausedTime = undefined;
|
||||
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { timerState } = domainObject.configuration;
|
||||
|
||||
return domainObject.type === 'timer' && timerState !== 'started';
|
||||
}
|
||||
}
|
57
src/plugins/timer/actions/StopTimerAction.js
Normal file
57
src/plugins/timer/actions/StopTimerAction.js
Normal file
@ -0,0 +1,57 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class StopTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Stop';
|
||||
this.key = 'timer.stop';
|
||||
this.description = 'Stop the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-box-round-corners';
|
||||
this.priority = 1;
|
||||
|
||||
this.openmct = openmct;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return new Error('Unable to run stop timer action. No domainObject provided.');
|
||||
}
|
||||
|
||||
const newConfiguration = { ...domainObject.configuration };
|
||||
newConfiguration.timerState = 'stopped';
|
||||
newConfiguration.timestamp = undefined;
|
||||
newConfiguration.pausedTime = undefined;
|
||||
|
||||
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
if (!domainObject || !domainObject.configuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { timerState } = domainObject.configuration;
|
||||
|
||||
return domainObject.type === 'timer' && timerState !== 'stopped';
|
||||
}
|
||||
}
|
233
src/plugins/timer/components/Timer.vue
Normal file
233
src/plugins/timer/components/Timer.vue
Normal file
@ -0,0 +1,233 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="c-timer u-style-receiver js-style-receiver"
|
||||
:class="[`is-${timerState}`]"
|
||||
>
|
||||
<div class="c-timer__controls">
|
||||
<button
|
||||
title="Reset"
|
||||
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"
|
||||
:class="[{'hide': timerState === 'stopped' }]"
|
||||
@click="restartTimer"
|
||||
></button>
|
||||
<button :title="timerStateButtonText"
|
||||
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major"
|
||||
:class="[timerStateButtonIcon]"
|
||||
@click="toggleStateButton"
|
||||
></button>
|
||||
</div>
|
||||
<div
|
||||
class="c-timer__direction"
|
||||
:class="[{'hide': !timerSign }, `icon-${timerSign}`]"
|
||||
></div>
|
||||
<div class="c-timer__value">{{ timeTextValue || "--:--:--" }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ticker from 'utils/clock/Ticker';
|
||||
|
||||
const moment = require("moment-timezone");
|
||||
const momentDurationFormatSetup = require("moment-duration-format");
|
||||
|
||||
momentDurationFormatSetup(moment);
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'currentView', 'objectPath'],
|
||||
props: {
|
||||
domainObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastTimestamp: undefined,
|
||||
active: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
configuration() {
|
||||
let configuration;
|
||||
if (this.domainObject && this.domainObject.configuration) {
|
||||
configuration = this.domainObject.configuration;
|
||||
}
|
||||
|
||||
return configuration;
|
||||
},
|
||||
relativeTimestamp() {
|
||||
let relativeTimestamp;
|
||||
if (this.configuration && this.configuration.timestamp) {
|
||||
relativeTimestamp = moment(this.configuration.timestamp).toDate();
|
||||
} else if (this.configuration && this.configuration.timestamp === undefined) {
|
||||
relativeTimestamp = undefined;
|
||||
}
|
||||
|
||||
return relativeTimestamp;
|
||||
},
|
||||
timeDelta() {
|
||||
return this.lastTimestamp - this.relativeTimestamp;
|
||||
},
|
||||
timeTextValue() {
|
||||
if (isNaN(this.timeDelta)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const toWholeSeconds = Math.abs(Math.floor(this.timeDelta / 1000) * 1000);
|
||||
|
||||
return moment.duration(toWholeSeconds, 'ms').format(this.format, { trim: false });
|
||||
},
|
||||
pausedTime() {
|
||||
let pausedTime;
|
||||
if (this.configuration && this.configuration.pausedTime) {
|
||||
pausedTime = moment(this.configuration.pausedTime).toDate();
|
||||
} else if (this.configuration && this.configuration.pausedTime === undefined) {
|
||||
pausedTime = undefined;
|
||||
}
|
||||
|
||||
return pausedTime;
|
||||
},
|
||||
timerState() {
|
||||
let timerState = 'started';
|
||||
if (this.configuration && this.configuration.timerState) {
|
||||
timerState = this.configuration.timerState;
|
||||
}
|
||||
|
||||
return timerState;
|
||||
},
|
||||
timerStateButtonText() {
|
||||
let buttonText = 'Pause';
|
||||
if (['paused', 'stopped'].includes(this.timerState)) {
|
||||
buttonText = 'Start';
|
||||
}
|
||||
|
||||
return buttonText;
|
||||
},
|
||||
timerStateButtonIcon() {
|
||||
let buttonIcon = 'icon-pause';
|
||||
if (['paused', 'stopped'].includes(this.timerState)) {
|
||||
buttonIcon = 'icon-play';
|
||||
}
|
||||
|
||||
return buttonIcon;
|
||||
},
|
||||
timerFormat() {
|
||||
let timerFormat = 'long';
|
||||
if (this.configuration && this.configuration.timerFormat) {
|
||||
timerFormat = this.configuration.timerFormat;
|
||||
}
|
||||
|
||||
return timerFormat;
|
||||
},
|
||||
format() {
|
||||
let format;
|
||||
if (this.timerFormat === 'long') {
|
||||
format = 'd[D] HH:mm:ss';
|
||||
}
|
||||
|
||||
if (this.timerFormat === 'short') {
|
||||
format = 'HH:mm:ss';
|
||||
}
|
||||
|
||||
return format;
|
||||
},
|
||||
timerType() {
|
||||
let timerType = null;
|
||||
if (isNaN(this.timeDelta)) {
|
||||
return timerType;
|
||||
}
|
||||
|
||||
if (this.timeDelta < 0) {
|
||||
timerType = 'countDown';
|
||||
} else if (this.timeDelta >= 1000) {
|
||||
timerType = 'countUp';
|
||||
}
|
||||
|
||||
return timerType;
|
||||
},
|
||||
timerSign() {
|
||||
let timerSign = null;
|
||||
if (this.timerType === 'countUp') {
|
||||
timerSign = 'plus';
|
||||
} else if (this.timerType === 'countDown') {
|
||||
timerSign = 'minus';
|
||||
}
|
||||
|
||||
return timerSign;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
if (this.configuration && this.configuration.timerState === undefined) {
|
||||
const timerAction = !this.relativeTimestamp ? 'stop' : 'start';
|
||||
this.triggerAction(`timer.${timerAction}`);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(this.tick);
|
||||
this.unlisten = ticker.listen(() => {
|
||||
this.openmct.objects.refresh(this.domainObject);
|
||||
});
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
this.active = false;
|
||||
if (this.unlisten) {
|
||||
this.unlisten();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tick() {
|
||||
const isTimerRunning = !['paused', 'stopped'].includes(this.timerState);
|
||||
if (isTimerRunning) {
|
||||
this.lastTimestamp = new Date();
|
||||
}
|
||||
|
||||
if (this.timerState === 'paused' && !this.lastTimestamp) {
|
||||
this.lastTimestamp = this.pausedTime;
|
||||
}
|
||||
|
||||
if (this.active) {
|
||||
window.requestAnimationFrame(this.tick);
|
||||
}
|
||||
},
|
||||
restartTimer() {
|
||||
this.triggerAction('timer.restart');
|
||||
},
|
||||
toggleStateButton() {
|
||||
if (this.timerState === 'started') {
|
||||
this.triggerAction('timer.pause');
|
||||
} else if (['paused', 'stopped'].includes(this.timerState)) {
|
||||
this.triggerAction('timer.start');
|
||||
}
|
||||
},
|
||||
triggerAction(actionKey) {
|
||||
const action = this.openmct.actions.getAction(actionKey);
|
||||
if (action) {
|
||||
action.invoke(this.objectPath, this.currentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
119
src/plugins/timer/plugin.js
Normal file
119
src/plugins/timer/plugin.js
Normal file
@ -0,0 +1,119 @@
|
||||
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import TimerViewProvider from './TimerViewProvider';
|
||||
|
||||
import PauseTimerAction from './actions/PauseTimerAction';
|
||||
import RestartTimerAction from './actions/RestartTimerAction';
|
||||
import StartTimerAction from './actions/StartTimerAction';
|
||||
import StopTimerAction from './actions/StopTimerAction';
|
||||
|
||||
export default function TimerPlugin() {
|
||||
return function install(openmct) {
|
||||
openmct.types.addType('timer', {
|
||||
name: 'Timer',
|
||||
description: 'A timer that counts up or down to a datetime. Timers can be started, stopped and reset whenever needed, and support a variety of display formats. Each Timer displays the same value to all users. Timers can be added to Display Layouts.',
|
||||
creatable: true,
|
||||
cssClass: 'icon-timer',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.configuration = {
|
||||
timerFormat: 'long',
|
||||
timestamp: undefined,
|
||||
timezone: 'UTC',
|
||||
timerState: undefined,
|
||||
pausedTime: undefined
|
||||
};
|
||||
},
|
||||
"form": [
|
||||
{
|
||||
"key": "timestamp",
|
||||
"control": "datetime",
|
||||
"name": "Target",
|
||||
property: [
|
||||
'configuration',
|
||||
'timestamp'
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "timerFormat",
|
||||
"name": "Display Format",
|
||||
"control": "select",
|
||||
"options": [
|
||||
{
|
||||
"value": "long",
|
||||
"name": "DDD hh:mm:ss"
|
||||
},
|
||||
{
|
||||
"value": "short",
|
||||
"name": "hh:mm:ss"
|
||||
}
|
||||
],
|
||||
property: [
|
||||
'configuration',
|
||||
'timerFormat'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
openmct.objectViews.addProvider(new TimerViewProvider(openmct));
|
||||
|
||||
openmct.actions.register(new PauseTimerAction(openmct));
|
||||
openmct.actions.register(new RestartTimerAction(openmct));
|
||||
openmct.actions.register(new StartTimerAction(openmct));
|
||||
openmct.actions.register(new StopTimerAction(openmct));
|
||||
|
||||
openmct.objects.addGetInterceptor({
|
||||
appliesTo: (identifier, domainObject) => {
|
||||
return domainObject && domainObject.type === 'timer';
|
||||
},
|
||||
invoke: (identifier, domainObject) => {
|
||||
if (domainObject.configuration) {
|
||||
return domainObject;
|
||||
}
|
||||
|
||||
const configuration = {};
|
||||
|
||||
if (domainObject.timerFormat) {
|
||||
configuration.timerFormat = domainObject.timerFormat;
|
||||
}
|
||||
|
||||
if (domainObject.timestamp) {
|
||||
configuration.timestamp = domainObject.timestamp;
|
||||
}
|
||||
|
||||
if (domainObject.timerState) {
|
||||
configuration.timerState = domainObject.timerState;
|
||||
}
|
||||
|
||||
if (domainObject.pausedTime) {
|
||||
configuration.pausedTime = domainObject.pausedTime;
|
||||
}
|
||||
|
||||
openmct.objects.mutate(domainObject, 'configuration', configuration);
|
||||
|
||||
return domainObject;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
}
|
354
src/plugins/timer/pluginSpec.js
Normal file
354
src/plugins/timer/pluginSpec.js
Normal file
@ -0,0 +1,354 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { createOpenMct, spyOnBuiltins, resetApplicationState } from 'utils/testing';
|
||||
import timerPlugin from './plugin';
|
||||
|
||||
import Vue from 'vue';
|
||||
|
||||
describe("Timer plugin:", () => {
|
||||
let openmct;
|
||||
let timerDefinition;
|
||||
let element;
|
||||
let child;
|
||||
let appHolder;
|
||||
|
||||
let timerDomainObject;
|
||||
|
||||
function setupTimer() {
|
||||
return new Promise((resolve, reject) => {
|
||||
timerDomainObject = {
|
||||
identifier: {
|
||||
key: 'timer',
|
||||
namespace: 'test-namespace'
|
||||
},
|
||||
type: 'timer'
|
||||
};
|
||||
|
||||
appHolder = document.createElement('div');
|
||||
appHolder.style.width = '640px';
|
||||
appHolder.style.height = '480px';
|
||||
document.body.appendChild(appHolder);
|
||||
|
||||
openmct = createOpenMct();
|
||||
|
||||
element = document.createElement('div');
|
||||
child = document.createElement('div');
|
||||
element.appendChild(child);
|
||||
|
||||
openmct.install(timerPlugin());
|
||||
|
||||
timerDefinition = openmct.types.get('timer').definition;
|
||||
timerDefinition.initialize(timerDomainObject);
|
||||
|
||||
openmct.on('start', resolve);
|
||||
openmct.start(appHolder);
|
||||
});
|
||||
}
|
||||
|
||||
describe("should still work if it's in the old format", () => {
|
||||
let timerViewProvider;
|
||||
let timerView;
|
||||
let timerViewObject;
|
||||
let mutableTimerObject;
|
||||
let timerObjectPath;
|
||||
const relativeTimestamp = 1634774400000; // Oct 21 2021, 12:00 AM
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupTimer();
|
||||
|
||||
timerViewObject = {
|
||||
identifier: {
|
||||
key: 'timer',
|
||||
namespace: 'test-namespace'
|
||||
},
|
||||
type: 'timer',
|
||||
id: "test-object",
|
||||
name: 'Timer',
|
||||
timerFormat: 'short',
|
||||
timestamp: relativeTimestamp,
|
||||
timerState: 'paused',
|
||||
pausedTime: relativeTimestamp
|
||||
};
|
||||
|
||||
const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
|
||||
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
|
||||
|
||||
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
|
||||
|
||||
timerObjectPath = [mutableTimerObject];
|
||||
timerView = timerViewProvider.view(mutableTimerObject, timerObjectPath);
|
||||
timerView.show(child);
|
||||
|
||||
await Vue.nextTick();
|
||||
});
|
||||
|
||||
it("should migrate old object properties to the configuration section", () => {
|
||||
openmct.objects.applyGetInterceptors(timerViewObject.identifier, timerViewObject);
|
||||
expect(timerViewObject.configuration.timerFormat).toBe('short');
|
||||
expect(timerViewObject.configuration.timestamp).toBe(relativeTimestamp);
|
||||
expect(timerViewObject.configuration.timerState).toBe('paused');
|
||||
expect(timerViewObject.configuration.pausedTime).toBe(relativeTimestamp);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Timer view:", () => {
|
||||
let timerViewProvider;
|
||||
let timerView;
|
||||
let timerViewObject;
|
||||
let mutableTimerObject;
|
||||
let timerObjectPath;
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupTimer();
|
||||
|
||||
spyOnBuiltins(['requestAnimationFrame']);
|
||||
window.requestAnimationFrame.and.callFake((cb) => setTimeout(cb, 500));
|
||||
const baseTimestamp = 1634688000000; // Oct 20, 2021, 12:00 AM
|
||||
const relativeTimestamp = 1634774400000; // Oct 21 2021, 12:00 AM
|
||||
|
||||
jasmine.clock().install();
|
||||
const baseTime = new Date(baseTimestamp);
|
||||
jasmine.clock().mockDate(baseTime);
|
||||
|
||||
timerViewObject = {
|
||||
...timerDomainObject,
|
||||
id: "test-object",
|
||||
name: 'Timer',
|
||||
configuration: {
|
||||
timerFormat: 'long',
|
||||
timestamp: relativeTimestamp,
|
||||
timezone: 'UTC',
|
||||
timerState: undefined,
|
||||
pausedTime: undefined
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(timerViewObject));
|
||||
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
|
||||
|
||||
const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
|
||||
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
|
||||
|
||||
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
|
||||
|
||||
timerObjectPath = [mutableTimerObject];
|
||||
timerView = timerViewProvider.view(mutableTimerObject, timerObjectPath);
|
||||
timerView.show(child);
|
||||
|
||||
await Vue.nextTick();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.clock().uninstall();
|
||||
timerView.destroy();
|
||||
openmct.objects.destroyMutable(mutableTimerObject);
|
||||
if (appHolder) {
|
||||
appHolder.remove();
|
||||
}
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("has name as Timer", () => {
|
||||
expect(timerDefinition.name).toEqual('Timer');
|
||||
});
|
||||
|
||||
it("is creatable", () => {
|
||||
expect(timerDefinition.creatable).toEqual(true);
|
||||
});
|
||||
|
||||
it("provides timer view", () => {
|
||||
expect(timerViewProvider).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders timer element", () => {
|
||||
const timerElement = element.querySelectorAll('.c-timer');
|
||||
expect(timerElement.length).toBe(1);
|
||||
});
|
||||
|
||||
it("renders major elements", () => {
|
||||
const timerElement = element.querySelector('.c-timer');
|
||||
const resetButton = timerElement.querySelector('.c-timer__ctrl-reset');
|
||||
const pausePlayButton = timerElement.querySelector('.c-timer__ctrl-pause-play');
|
||||
const timerDirectionIcon = timerElement.querySelector('.c-timer__direction');
|
||||
const timerValue = timerElement.querySelector('.c-timer__value');
|
||||
const hasMajorElements = Boolean(resetButton && pausePlayButton && timerDirectionIcon && timerValue);
|
||||
|
||||
expect(hasMajorElements).toBe(true);
|
||||
});
|
||||
|
||||
it("gets errors from actions if configuration is not passed", async () => {
|
||||
await Vue.nextTick();
|
||||
const objectPath = _.cloneDeep(timerObjectPath);
|
||||
delete objectPath[0].configuration;
|
||||
|
||||
let action = openmct.actions.getAction('timer.start');
|
||||
let actionResults = action.invoke(objectPath);
|
||||
let actionFilterWithoutConfig = action.appliesTo(objectPath);
|
||||
await openmct.objects.mutate(timerObjectPath[0], 'configuration', { timerState: 'started' });
|
||||
let actionFilterWithConfig = action.appliesTo(timerObjectPath);
|
||||
|
||||
let actionError = new Error('Unable to run start timer action. No domainObject provided.');
|
||||
expect(actionResults).toEqual(actionError);
|
||||
expect(actionFilterWithoutConfig).toBe(undefined);
|
||||
expect(actionFilterWithConfig).toBe(false);
|
||||
|
||||
action = openmct.actions.getAction('timer.stop');
|
||||
actionResults = action.invoke(objectPath);
|
||||
actionFilterWithoutConfig = action.appliesTo(objectPath);
|
||||
await openmct.objects.mutate(timerObjectPath[0], 'configuration', { timerState: 'stopped' });
|
||||
actionFilterWithConfig = action.appliesTo(timerObjectPath);
|
||||
|
||||
actionError = new Error('Unable to run stop timer action. No domainObject provided.');
|
||||
expect(actionResults).toEqual(actionError);
|
||||
expect(actionFilterWithoutConfig).toBe(undefined);
|
||||
expect(actionFilterWithConfig).toBe(false);
|
||||
|
||||
action = openmct.actions.getAction('timer.pause');
|
||||
actionResults = action.invoke(objectPath);
|
||||
actionFilterWithoutConfig = action.appliesTo(objectPath);
|
||||
await openmct.objects.mutate(timerObjectPath[0], 'configuration', { timerState: 'paused' });
|
||||
actionFilterWithConfig = action.appliesTo(timerObjectPath);
|
||||
|
||||
actionError = new Error('Unable to run pause timer action. No domainObject provided.');
|
||||
expect(actionResults).toEqual(actionError);
|
||||
expect(actionFilterWithoutConfig).toBe(undefined);
|
||||
expect(actionFilterWithConfig).toBe(false);
|
||||
|
||||
action = openmct.actions.getAction('timer.restart');
|
||||
actionResults = action.invoke(objectPath);
|
||||
actionFilterWithoutConfig = action.appliesTo(objectPath);
|
||||
await openmct.objects.mutate(timerObjectPath[0], 'configuration', { timerState: 'stopped' });
|
||||
actionFilterWithConfig = action.appliesTo(timerObjectPath);
|
||||
|
||||
actionError = new Error('Unable to run restart timer action. No domainObject provided.');
|
||||
expect(actionResults).toEqual(actionError);
|
||||
expect(actionFilterWithoutConfig).toBe(undefined);
|
||||
expect(actionFilterWithConfig).toBe(false);
|
||||
});
|
||||
|
||||
it("displays a started timer ticking down to a future date", async () => {
|
||||
const newBaseTime = 1634774400000; // Oct 21 2021, 12:00 AM
|
||||
openmct.objects.mutate(timerViewObject, 'configuration.timestamp', newBaseTime);
|
||||
|
||||
jasmine.clock().tick(5000);
|
||||
await Vue.nextTick();
|
||||
|
||||
const timerElement = element.querySelector('.c-timer');
|
||||
const timerPausePlayButton = timerElement.querySelector('.c-timer__ctrl-pause-play');
|
||||
const timerDirectionIcon = timerElement.querySelector('.c-timer__direction');
|
||||
const timerValue = timerElement.querySelector('.c-timer__value').innerText;
|
||||
|
||||
expect(timerPausePlayButton.classList.contains('icon-pause')).toBe(true);
|
||||
expect(timerDirectionIcon.classList.contains('icon-minus')).toBe(true);
|
||||
expect(timerValue).toBe('0D 23:59:55');
|
||||
});
|
||||
|
||||
it("displays a started timer ticking up from a past date", async () => {
|
||||
const newBaseTime = 1634601600000; // Oct 19, 2021, 12:00 AM
|
||||
openmct.objects.mutate(timerViewObject, 'configuration.timestamp', newBaseTime);
|
||||
|
||||
jasmine.clock().tick(5000);
|
||||
await Vue.nextTick();
|
||||
|
||||
const timerElement = element.querySelector('.c-timer');
|
||||
const timerPausePlayButton = timerElement.querySelector('.c-timer__ctrl-pause-play');
|
||||
const timerDirectionIcon = timerElement.querySelector('.c-timer__direction');
|
||||
const timerValue = timerElement.querySelector('.c-timer__value').innerText;
|
||||
|
||||
expect(timerPausePlayButton.classList.contains('icon-pause')).toBe(true);
|
||||
expect(timerDirectionIcon.classList.contains('icon-plus')).toBe(true);
|
||||
expect(timerValue).toBe('1D 00:00:05');
|
||||
});
|
||||
|
||||
it("displays a paused timer correctly in the DOM", async () => {
|
||||
jasmine.clock().tick(5000);
|
||||
await Vue.nextTick();
|
||||
|
||||
let action = openmct.actions.getAction('timer.pause');
|
||||
if (action) {
|
||||
action.invoke(timerObjectPath, timerView);
|
||||
}
|
||||
|
||||
await Vue.nextTick();
|
||||
const timerElement = element.querySelector('.c-timer');
|
||||
const timerPausePlayButton = timerElement.querySelector('.c-timer__ctrl-pause-play');
|
||||
let timerValue = timerElement.querySelector('.c-timer__value').innerText;
|
||||
|
||||
expect(timerPausePlayButton.classList.contains('icon-play')).toBe(true);
|
||||
expect(timerValue).toBe('0D 23:59:55');
|
||||
|
||||
jasmine.clock().tick(5000);
|
||||
await Vue.nextTick();
|
||||
expect(timerValue).toBe('0D 23:59:55');
|
||||
|
||||
action = openmct.actions.getAction('timer.start');
|
||||
if (action) {
|
||||
action.invoke(timerObjectPath, timerView);
|
||||
}
|
||||
|
||||
await Vue.nextTick();
|
||||
action = openmct.actions.getAction('timer.pause');
|
||||
if (action) {
|
||||
action.invoke(timerObjectPath, timerView);
|
||||
}
|
||||
|
||||
await Vue.nextTick();
|
||||
timerValue = timerElement.querySelector('.c-timer__value').innerText;
|
||||
expect(timerValue).toBe('1D 00:00:00');
|
||||
});
|
||||
|
||||
it("displays a stopped timer correctly in the DOM", async () => {
|
||||
const action = openmct.actions.getAction('timer.stop');
|
||||
if (action) {
|
||||
action.invoke(timerObjectPath, timerView);
|
||||
}
|
||||
|
||||
await Vue.nextTick();
|
||||
const timerElement = element.querySelector('.c-timer');
|
||||
const timerValue = timerElement.querySelector('.c-timer__value').innerText;
|
||||
const timerResetButton = timerElement.querySelector('.c-timer__ctrl-reset');
|
||||
const timerPausePlayButton = timerElement.querySelector('.c-timer__ctrl-pause-play');
|
||||
|
||||
expect(timerResetButton.classList.contains('hide')).toBe(true);
|
||||
expect(timerPausePlayButton.classList.contains('icon-play')).toBe(true);
|
||||
expect(timerValue).toBe('--:--:--');
|
||||
});
|
||||
|
||||
it("displays a restarted timer correctly in the DOM", async () => {
|
||||
const action = openmct.actions.getAction('timer.restart');
|
||||
if (action) {
|
||||
action.invoke(timerObjectPath, timerView);
|
||||
}
|
||||
|
||||
jasmine.clock().tick(5000);
|
||||
await Vue.nextTick();
|
||||
const timerElement = element.querySelector('.c-timer');
|
||||
const timerValue = timerElement.querySelector('.c-timer__value').innerText;
|
||||
const timerPausePlayButton = timerElement.querySelector('.c-timer__ctrl-pause-play');
|
||||
|
||||
expect(timerPausePlayButton.classList.contains('icon-pause')).toBe(true);
|
||||
expect(timerValue).toBe('0D 00:00:05');
|
||||
});
|
||||
});
|
||||
});
|
81
src/utils/clock/Ticker.js
Normal file
81
src/utils/clock/Ticker.js
Normal file
@ -0,0 +1,81 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
class Ticker {
|
||||
constructor() {
|
||||
this.callbacks = [];
|
||||
this.last = new Date() - 1000;
|
||||
|
||||
this.tick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls functions every second, as close to the actual second
|
||||
* tick as is feasible.
|
||||
* @constructor
|
||||
* @memberof utils/clock
|
||||
*/
|
||||
tick() {
|
||||
const timestamp = new Date();
|
||||
const millis = timestamp % 1000;
|
||||
|
||||
// Only update callbacks if a second has actually passed.
|
||||
if (timestamp >= this.last + 1000) {
|
||||
this.callbacks.forEach(function (callback) {
|
||||
callback(timestamp);
|
||||
});
|
||||
this.last = timestamp - millis;
|
||||
}
|
||||
|
||||
// Try to update at exactly the next second
|
||||
setTimeout(() => {
|
||||
this.tick();
|
||||
}, 1000 - millis, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for clock ticks. The provided callback will
|
||||
* be invoked with the current timestamp (in milliseconds
|
||||
* since Jan 1 1970) at regular intervals, as near to the
|
||||
* second boundary as possible.
|
||||
*
|
||||
* @param {Function} callback callback to invoke
|
||||
* @returns {Function} a function to unregister this listener
|
||||
*/
|
||||
listen(callback) {
|
||||
this.callbacks.push(callback);
|
||||
|
||||
// Provide immediate feedback
|
||||
callback(this.last);
|
||||
|
||||
// Provide a deregistration function
|
||||
return () => {
|
||||
this.callbacks = this.callbacks.filter(function (cb) {
|
||||
return cb !== callback;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let ticker = new Ticker();
|
||||
|
||||
export default ticker;
|
Loading…
Reference in New Issue
Block a user