From 7fc66e2de80e060ff428b2b8153ae3f640c34096 Mon Sep 17 00:00:00 2001 From: DJ Date: Sat, 14 Jan 2017 16:41:54 -0600 Subject: [PATCH 01/12] [Clocks/Timers] renaming of Abstract class renaming AbstractStartTimerAction to AbstractTime --- ...AbstractStartTimerAction.js => AbstractTimerAction.js} | 6 +++--- platform/features/clock/src/actions/RestartTimerAction.js | 8 ++++---- platform/features/clock/src/actions/StartTimerAction.js | 8 ++++---- ...StartTimerActionSpec.js => AbstractTimerActionSpec.js} | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) rename platform/features/clock/src/actions/{AbstractStartTimerAction.js => AbstractTimerAction.js} (93%) rename platform/features/clock/test/actions/{AbstractStartTimerActionSpec.js => AbstractTimerActionSpec.js} (94%) diff --git a/platform/features/clock/src/actions/AbstractStartTimerAction.js b/platform/features/clock/src/actions/AbstractTimerAction.js similarity index 93% rename from platform/features/clock/src/actions/AbstractStartTimerAction.js rename to platform/features/clock/src/actions/AbstractTimerAction.js index dc55f446f4..f507a4f435 100644 --- a/platform/features/clock/src/actions/AbstractStartTimerAction.js +++ b/platform/features/clock/src/actions/AbstractTimerAction.js @@ -40,12 +40,12 @@ define( * time (typically wrapping `Date.now`) * @param {ActionContext} context the context for this action */ - function AbstractStartTimerAction(now, context) { + function AbstractTimerAction(now, context) { this.domainObject = context.domainObject; this.now = now; } - AbstractStartTimerAction.prototype.perform = function () { + AbstractTimerAction.prototype.perform = function () { var domainObject = this.domainObject, now = this.now; @@ -56,6 +56,6 @@ define( return domainObject.useCapability('mutation', setTimestamp); }; - return AbstractStartTimerAction; + return AbstractTimerAction; } ); diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js index bb12cb1392..6245f1324b 100644 --- a/platform/features/clock/src/actions/RestartTimerAction.js +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ['./AbstractStartTimerAction'], - function (AbstractStartTimerAction) { + ['./AbstractTimerAction'], + function (AbstractTimerAction) { /** * Implements the "Restart at 0" action. @@ -39,11 +39,11 @@ define( * @param {ActionContext} context the context for this action */ function RestartTimerAction(now, context) { - AbstractStartTimerAction.apply(this, [now, context]); + AbstractTimerAction.apply(this, [now, context]); } RestartTimerAction.prototype = - Object.create(AbstractStartTimerAction.prototype); + Object.create(AbstractTimerAction.prototype); RestartTimerAction.appliesTo = function (context) { var model = diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index cf3952a99d..0336bebc6b 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ['./AbstractStartTimerAction'], - function (AbstractStartTimerAction) { + ['./AbstractTimerAction'], + function (AbstractTimerAction) { /** * Implements the "Start" action for timers. @@ -39,11 +39,11 @@ define( * @param {ActionContext} context the context for this action */ function StartTimerAction(now, context) { - AbstractStartTimerAction.apply(this, [now, context]); + AbstractTimerAction.apply(this, [now, context]); } StartTimerAction.prototype = - Object.create(AbstractStartTimerAction.prototype); + Object.create(AbstractTimerAction.prototype); StartTimerAction.appliesTo = function (context) { var model = diff --git a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js b/platform/features/clock/test/actions/AbstractTimerActionSpec.js similarity index 94% rename from platform/features/clock/test/actions/AbstractStartTimerActionSpec.js rename to platform/features/clock/test/actions/AbstractTimerActionSpec.js index 6478d07877..74d8763ced 100644 --- a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js +++ b/platform/features/clock/test/actions/AbstractTimerActionSpec.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ["../../src/actions/AbstractStartTimerAction"], - function (AbstractStartTimerAction) { + ["../../src/actions/AbstractTimerAction"], + function (AbstractTimerAction) { describe("A timer's start/restart action", function () { var mockNow, @@ -54,7 +54,7 @@ define( testModel = {}; - action = new AbstractStartTimerAction(mockNow, { + action = new AbstractTimerAction(mockNow, { domainObject: mockDomainObject }); }); From 9df59522d9e1c1482357e44b0f87c18ab706128e Mon Sep 17 00:00:00 2001 From: DJ Date: Sat, 14 Jan 2017 16:42:44 -0600 Subject: [PATCH 02/12] =?UTF-8?q?[Clocks=20/=20Timers]=C2=A0=20adding=20ab?= =?UTF-8?q?ility=20to=20stop=20added=20the=20stop=20timer=20menu=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../icomoon-project-openmct-symbols-16px.json | 33 +++++++ .../fonts/symbols/openmct-symbols-16px.svg | 1 + .../commonUI/general/res/sass/_glyphs.scss | 2 + platform/features/clock/bundle.js | 13 +++ .../clock/src/actions/StopTimerAction.js | 73 ++++++++++++++++ .../clock/test/actions/StopTimerActionSpec.js | 86 +++++++++++++++++++ 6 files changed, 208 insertions(+) create mode 100644 platform/features/clock/src/actions/StopTimerAction.js create mode 100644 platform/features/clock/test/actions/StopTimerActionSpec.js diff --git a/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json b/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json index 68f8f5ea5e..22c74333b8 100644 --- a/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json +++ b/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json @@ -811,6 +811,14 @@ "id": 29, "code": 921897, "tempChar": "" + }, + { + "order": 121, + "prevSize": 24, + "name": "icon-stop", + "id": 89, + "code": 921637, + "tempChar": "" } ], "metadata": { @@ -3085,6 +3093,31 @@ {} ] } + }, + { + "paths": [ + "M0 0h24v24H0z" + ], + "grid": 16, + "tags": [ + "icon-stop" + ], + "defaultCode": 114, + "id": 89, + "attrs": [ + { + "fill": "rgb(0, 161, 75)" + } + ], + "isMulticolor": false, + "isMulticolor2": false, + "colorPermutations": { + "1161751207457516161751": [ + { + "f": 1 + } + ] + } } ], "colorThemes": [ diff --git a/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg b/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg index 0a33d43fa3..458504f884 100755 --- a/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg +++ b/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg @@ -107,4 +107,5 @@ + \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_glyphs.scss b/platform/commonUI/general/res/sass/_glyphs.scss index c8a369e6e7..8ea5e3c704 100644 --- a/platform/commonUI/general/res/sass/_glyphs.scss +++ b/platform/commonUI/general/res/sass/_glyphs.scss @@ -107,6 +107,7 @@ $glyph-icon-timeline: '\e1126'; $glyph-icon-timer: '\e1127'; $glyph-icon-topic: '\e1128'; $glyph-icon-box-with-dashed-lines: '\e1129'; +$glyph-icon-stop: '\e1130'; /************************** 16 PX CLASSES */ @@ -210,6 +211,7 @@ $glyph-icon-box-with-dashed-lines: '\e1129'; .icon-timer { @include glyph($glyph-icon-timer); } .icon-topic { @include glyph($glyph-icon-topic); } .icon-box-with-dashed-lines { @include glyph($glyph-icon-box-with-dashed-lines); } +.icon-stop { @include glyph($glyph-icon-stop); } /************************** 12 PX CLASSES */ diff --git a/platform/features/clock/bundle.js b/platform/features/clock/bundle.js index 5699527e36..8dae9c07ba 100644 --- a/platform/features/clock/bundle.js +++ b/platform/features/clock/bundle.js @@ -28,6 +28,7 @@ define([ "./src/controllers/RefreshingController", "./src/actions/StartTimerAction", "./src/actions/RestartTimerAction", + "./src/actions/StopTimerAction", "text!./res/templates/clock.html", "text!./res/templates/timer.html", 'legacyRegistry' @@ -39,6 +40,7 @@ define([ RefreshingController, StartTimerAction, RestartTimerAction, + StopTimerAction, clockTemplate, timerTemplate, legacyRegistry @@ -149,6 +151,17 @@ define([ "name": "Restart at 0", "cssclass": "icon-refresh", "priority": "preferred" + }, + { + "key": "timer.stop", + "implementation": StopTimerAction, + "depends": [ + "now" + ], + "category": "contextual", + "name": "Stop", + "cssclass": "icon-box", + "priority": "preferred" } ], "types": [ diff --git a/platform/features/clock/src/actions/StopTimerAction.js b/platform/features/clock/src/actions/StopTimerAction.js new file mode 100644 index 0000000000..1c56d7a1e0 --- /dev/null +++ b/platform/features/clock/src/actions/StopTimerAction.js @@ -0,0 +1,73 @@ +/***************************************************************************** + * 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( + ['./AbstractTimerAction'], + function (AbstractTimerAction) { + + /** + * Implements the "Start" action for timers. + * + * Sets the reference timestamp in a timer to the current + * time, such that it begins counting up. + * + * @extends {platform/features/clock.AbstractTimerAction} + * @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) { + AbstractTimerAction.apply(this, [now, context]); + } + + StopTimerAction.prototype = + Object.create(AbstractTimerAction.prototype); + + 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.timestamp !== undefined; + }; + + StopTimerAction.prototype.perform = function () { + var domainObject = this.domainObject; + + function setTimestamp(model) { + model.timestamp = undefined; + } + + return domainObject.useCapability('mutation', setTimestamp); + }; + + return StopTimerAction; + + } +); diff --git a/platform/features/clock/test/actions/StopTimerActionSpec.js b/platform/features/clock/test/actions/StopTimerActionSpec.js new file mode 100644 index 0000000000..6b86a0bdcf --- /dev/null +++ b/platform/features/clock/test/actions/StopTimerActionSpec.js @@ -0,0 +1,86 @@ +/***************************************************************************** + * 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)); + } + }; + } + + beforeEach(function () { + mockNow = jasmine.createSpy('now'); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getCapability', 'useCapability', 'getModel'] + ); + + mockDomainObject.useCapability.andCallFake(function (c, v) { + if (c === 'mutation') { + testModel = v(testModel) || testModel; + return asPromise(true); + } + }); + mockDomainObject.getModel.andCallFake(function () { + return testModel; + }); + + testModel = {}; + testContext = { domainObject: mockDomainObject }; + + action = new StopTimerAction(mockNow, testContext); + }); + + it("updates the model with a timestamp", function () { + mockNow.andReturn(12000); + action.perform(); + expect(testModel.timestamp).toEqual(12000); + }); + + it("applies only to timers without a target time", function () { + testModel.type = 'timer'; + testModel.timestamp = 12000; + expect(StopTimerAction.appliesTo(testContext)).toBeTruthy(); + + testModel.type = 'timer'; + testModel.timestamp = undefined; + expect(StopTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.type = 'clock'; + testModel.timestamp = 12000; + expect(StopTimerAction.appliesTo(testContext)).toBeFalsy(); + }); + }); + } +); From 165bd4f6389686c46eb9ecf6680035686eb9cbe6 Mon Sep 17 00:00:00 2001 From: DJ Date: Sat, 14 Jan 2017 16:43:20 -0600 Subject: [PATCH 03/12] =?UTF-8?q?[Clocks=20/=20Timers]=C2=A0=20adding=20ab?= =?UTF-8?q?ility=20to=20pause=20and=20resume=20added=20the=20alternating?= =?UTF-8?q?=20pause=20and=20resume=20timer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platform/features/clock/bundle.js | 36 ++++++ .../clock/src/actions/AbstractTimerAction.js | 7 +- .../clock/src/actions/PauseTimerAction.js | 78 +++++++++++++ .../clock/src/actions/ResumeTimerAction.js | 85 +++++++++++++++ .../clock/src/actions/StartTimerAction.js | 1 - .../clock/src/actions/StopTimerAction.js | 8 +- .../clock/src/controllers/TimerController.js | 17 ++- .../test/actions/PauseTimerActionSpec.js | 103 ++++++++++++++++++ .../test/actions/ResumeActionTimerSpec.js | 103 ++++++++++++++++++ 9 files changed, 431 insertions(+), 7 deletions(-) create mode 100644 platform/features/clock/src/actions/PauseTimerAction.js create mode 100644 platform/features/clock/src/actions/ResumeTimerAction.js create mode 100644 platform/features/clock/test/actions/PauseTimerActionSpec.js create mode 100644 platform/features/clock/test/actions/ResumeActionTimerSpec.js diff --git a/platform/features/clock/bundle.js b/platform/features/clock/bundle.js index 8dae9c07ba..cf71ec9e38 100644 --- a/platform/features/clock/bundle.js +++ b/platform/features/clock/bundle.js @@ -29,6 +29,8 @@ define([ "./src/actions/StartTimerAction", "./src/actions/RestartTimerAction", "./src/actions/StopTimerAction", + "./src/actions/PauseTimerAction", + "./src/actions/ResumeTimerAction", "text!./res/templates/clock.html", "text!./res/templates/timer.html", 'legacyRegistry' @@ -41,6 +43,8 @@ define([ StartTimerAction, RestartTimerAction, StopTimerAction, + PauseTimerAction, + ResumeTimerAction, clockTemplate, timerTemplate, legacyRegistry @@ -152,6 +156,28 @@ define([ "cssclass": "icon-refresh", "priority": "preferred" }, + { + "key": "timer.pause", + "implementation": PauseTimerAction, + "depends": [ + "now" + ], + "category": "contextual", + "name": "Pause", + "cssclass": "icon-pause", + "priority": "preferred" + }, + { + "key": "timer.resume", + "implementation": ResumeTimerAction, + "depends": [ + "now" + ], + "category": "contextual", + "name": "Resume", + "cssclass": "icon-play", + "priority": "preferred" + }, { "key": "timer.stop", "implementation": StopTimerAction, @@ -251,6 +277,16 @@ define([ "name": "hh:mm:ss" } ] + }, + { + "key": "paused", + "control": "boolean", + "name": "PauseCheck" + }, + { + "key": "pausedTime", + "control": "long", + "name": "TimeOfPause" } ], "model": { diff --git a/platform/features/clock/src/actions/AbstractTimerAction.js b/platform/features/clock/src/actions/AbstractTimerAction.js index f507a4f435..58de84dfae 100644 --- a/platform/features/clock/src/actions/AbstractTimerAction.js +++ b/platform/features/clock/src/actions/AbstractTimerAction.js @@ -53,7 +53,12 @@ define( model.timestamp = now(); } - return domainObject.useCapability('mutation', setTimestamp); + function setPaused(model) { + model.paused = false; + } + + return domainObject.useCapability('mutation', setTimestamp) && + domainObject.useCapability('mutation', setPaused); }; return AbstractTimerAction; diff --git a/platform/features/clock/src/actions/PauseTimerAction.js b/platform/features/clock/src/actions/PauseTimerAction.js new file mode 100644 index 0000000000..efd54830e4 --- /dev/null +++ b/platform/features/clock/src/actions/PauseTimerAction.js @@ -0,0 +1,78 @@ +/***************************************************************************** + * 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( + ['./AbstractTimerAction'], + function (AbstractTimerAction) { + + /** + * Implements the "Start" action for timers. + * + * Sets the reference timestamp in a timer to the current + * time, such that it begins counting up. + * + * @extends {platform/features/clock.AbstractTimerAction} + * @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) { + AbstractTimerAction.apply(this, [now, context]); + } + + PauseTimerAction.prototype = + Object.create(AbstractTimerAction.prototype); + + PauseTimerAction.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.timestamp !== undefined && !model.paused; + }; + + PauseTimerAction.prototype.perform = function () { + var domainObject = this.domainObject, + now = this.now; + + function setPaused(model) { + model.paused = true; + } + + function setPausedTime(model) { + model.pausedTime = now(); + } + + return domainObject.useCapability('mutation', setPaused) && + domainObject.useCapability('mutation', setPausedTime); + }; + + return PauseTimerAction; + } +); diff --git a/platform/features/clock/src/actions/ResumeTimerAction.js b/platform/features/clock/src/actions/ResumeTimerAction.js new file mode 100644 index 0000000000..7e5c160d03 --- /dev/null +++ b/platform/features/clock/src/actions/ResumeTimerAction.js @@ -0,0 +1,85 @@ +/***************************************************************************** + * 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( + ['./AbstractTimerAction'], + function (AbstractTimerAction) { + + /** + * Implements the "Start" action for timers. + * + * Sets the reference timestamp in a timer to the current + * time, such that it begins counting up. + * + * @extends {platform/features/clock.AbstractTimerAction} + * @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 ResumeTimerAction(now, context) { + AbstractTimerAction.apply(this, [now, context]); + } + + ResumeTimerAction.prototype = + Object.create(AbstractTimerAction.prototype); + + ResumeTimerAction.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.timestamp !== undefined && + model.paused; + }; + + ResumeTimerAction.prototype.perform = function () { + var domainObject = this.domainObject, + now = this.now; + + function setPaused(model) { + model.paused = false; + } + + function setTimestamp(model) { + var timeShift = now() - model.pausedTime; + model.timestamp = model.timestamp + timeShift; + } + + function setPausedTime(model) { + model.pausedTime = undefined; + } + + return domainObject.useCapability('mutation', setPaused) && + domainObject.useCapability('mutation', setTimestamp) && + domainObject.useCapability('mutation', setPausedTime); + }; + + return ResumeTimerAction; + } +); \ No newline at end of file diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index 0336bebc6b..42abe17247 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -57,6 +57,5 @@ define( }; return StartTimerAction; - } ); diff --git a/platform/features/clock/src/actions/StopTimerAction.js b/platform/features/clock/src/actions/StopTimerAction.js index 1c56d7a1e0..d3d19b160a 100644 --- a/platform/features/clock/src/actions/StopTimerAction.js +++ b/platform/features/clock/src/actions/StopTimerAction.js @@ -64,10 +64,14 @@ define( model.timestamp = undefined; } - return domainObject.useCapability('mutation', setTimestamp); + function setPaused(model) { + model.paused = false; + } + + return domainObject.useCapability('mutation', setTimestamp) && + domainObject.useCapability('mutation', setPaused); }; return StopTimerAction; - } ); diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index a95ff3d6a4..e2cd2bdb48 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -74,7 +74,15 @@ define( formatKey = model.timerFormat, actionCapability = domainObject.getCapability('action'), actionKey = (timestamp === undefined) ? - 'timer.start' : 'timer.restart'; + 'timer.start' : 'timer.restart'; + + self.paused = model.paused; + self.pausedTime = model.pausedTime; + + //if paused on startup show last known position + if (self.paused && !lastTimestamp){ + lastTimestamp = self.pausedTime; + } updateFormat(formatKey); updateTimestamp(timestamp); @@ -98,8 +106,11 @@ define( function tick() { var lastSign = self.signValue, lastText = self.textValue; - lastTimestamp = now(); - update(); + + if (!self.paused) { + lastTimestamp = now(); + update(); + } // We're running in an animation frame, not in a digest cycle. // We need to trigger a digest cycle if our displayable data // changes. diff --git a/platform/features/clock/test/actions/PauseTimerActionSpec.js b/platform/features/clock/test/actions/PauseTimerActionSpec.js new file mode 100644 index 0000000000..0f8cc1192f --- /dev/null +++ b/platform/features/clock/test/actions/PauseTimerActionSpec.js @@ -0,0 +1,103 @@ +/***************************************************************************** + * 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)); + } + }; + } + + beforeEach(function () { + mockNow = jasmine.createSpy('now'); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getCapability', 'useCapability', 'getModel'] + ); + + mockDomainObject.useCapability.andCallFake(function (c, v) { + if (c === 'mutation') { + testModel = v(testModel) || testModel; + return asPromise(true); + } + }); + mockDomainObject.getModel.andCallFake(function () { + return testModel; + }); + + testModel = {}; + testContext = { domainObject: mockDomainObject }; + + action = new PauseTimerAction(mockNow, testContext); + }); + + it("updates the model with a timestamp", function () { + mockNow.andReturn(12000); + action.perform(); + expect(testModel.timestamp).toEqual(12000); + }); + + it("applies only to timers without a target time", function () { + //Timer is on + testModel.type = 'timer'; + testModel.timestamp = 12000; + + testModel.paused = true; + expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.paused = false; + expect(PauseTimerAction.appliesTo(testContext)).toBeTruthy(); + + //Timer has not started + testModel.timestamp = undefined; + + testModel.paused = true; + expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.paused = false; + expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + + //Timer is actually a clock + testModel.type = 'clock'; + testModel.timestamp = 12000; + + testModel.paused = true; + expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.paused = false; + expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + }); + }); + } +); diff --git a/platform/features/clock/test/actions/ResumeActionTimerSpec.js b/platform/features/clock/test/actions/ResumeActionTimerSpec.js new file mode 100644 index 0000000000..10f6d943dd --- /dev/null +++ b/platform/features/clock/test/actions/ResumeActionTimerSpec.js @@ -0,0 +1,103 @@ +/***************************************************************************** + * 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/ResumeTimerAction"], + function (ResumeTimerAction) { + + describe("A timer's Resume action", function () { + var mockNow, + mockDomainObject, + testModel, + testContext, + action; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + beforeEach(function () { + mockNow = jasmine.createSpy('now'); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getCapability', 'useCapability', 'getModel'] + ); + + mockDomainObject.useCapability.andCallFake(function (c, v) { + if (c === 'mutation') { + testModel = v(testModel) || testModel; + return asPromise(true); + } + }); + mockDomainObject.getModel.andCallFake(function () { + return testModel; + }); + + testModel = {}; + testContext = { domainObject: mockDomainObject }; + + action = new ResumeTimerAction(mockNow, testContext); + }); + + it("updates the model with a timestamp", function () { + mockNow.andReturn(12000); + action.perform(); + expect(testModel.timestamp).toEqual(12000); + }); + + it("applies only to timers without a target time", function () { + //Timer is on + testModel.type = 'timer'; + testModel.timestamp = 12000; + + testModel.paused = true; + expect(ResumeTimerAction.appliesTo(testContext)).toBeTruthy(); + + testModel.paused = false; + expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); + + //Timer has not started + testModel.timestamp = undefined; + + testModel.paused = true; + expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.paused = false; + expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); + + //Timer is actually a clock + testModel.type = 'clock'; + testModel.timestamp = 12000; + + testModel.paused = true; + expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.paused = false; + expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); + }); + }); + } +); From d04bdd268537ceebc3aa63dac44649b2e610dff8 Mon Sep 17 00:00:00 2001 From: DJ Date: Tue, 17 Jan 2017 19:55:32 -0600 Subject: [PATCH 04/12] [Timer] Removed icon-stop Removed icon-stop, identical to icon-box --- .../icomoon-project-openmct-symbols-16px.json | 33 ------------------- .../fonts/symbols/openmct-symbols-16px.svg | 1 - .../commonUI/general/res/sass/_glyphs.scss | 2 -- 3 files changed, 36 deletions(-) diff --git a/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json b/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json index 22c74333b8..68f8f5ea5e 100644 --- a/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json +++ b/platform/commonUI/general/res/fonts/symbols/icomoon-project-openmct-symbols-16px.json @@ -811,14 +811,6 @@ "id": 29, "code": 921897, "tempChar": "" - }, - { - "order": 121, - "prevSize": 24, - "name": "icon-stop", - "id": 89, - "code": 921637, - "tempChar": "" } ], "metadata": { @@ -3093,31 +3085,6 @@ {} ] } - }, - { - "paths": [ - "M0 0h24v24H0z" - ], - "grid": 16, - "tags": [ - "icon-stop" - ], - "defaultCode": 114, - "id": 89, - "attrs": [ - { - "fill": "rgb(0, 161, 75)" - } - ], - "isMulticolor": false, - "isMulticolor2": false, - "colorPermutations": { - "1161751207457516161751": [ - { - "f": 1 - } - ] - } } ], "colorThemes": [ diff --git a/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg b/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg index 458504f884..0a33d43fa3 100755 --- a/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg +++ b/platform/commonUI/general/res/fonts/symbols/openmct-symbols-16px.svg @@ -107,5 +107,4 @@ - \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_glyphs.scss b/platform/commonUI/general/res/sass/_glyphs.scss index 8ea5e3c704..c8a369e6e7 100644 --- a/platform/commonUI/general/res/sass/_glyphs.scss +++ b/platform/commonUI/general/res/sass/_glyphs.scss @@ -107,7 +107,6 @@ $glyph-icon-timeline: '\e1126'; $glyph-icon-timer: '\e1127'; $glyph-icon-topic: '\e1128'; $glyph-icon-box-with-dashed-lines: '\e1129'; -$glyph-icon-stop: '\e1130'; /************************** 16 PX CLASSES */ @@ -211,7 +210,6 @@ $glyph-icon-stop: '\e1130'; .icon-timer { @include glyph($glyph-icon-timer); } .icon-topic { @include glyph($glyph-icon-topic); } .icon-box-with-dashed-lines { @include glyph($glyph-icon-box-with-dashed-lines); } -.icon-stop { @include glyph($glyph-icon-stop); } /************************** 12 PX CLASSES */ From ecf1bac5c7ad6f9888d961ce411fcf6a51754ace Mon Sep 17 00:00:00 2001 From: DJ Date: Wed, 18 Jan 2017 07:08:54 -0600 Subject: [PATCH 05/12] [Timer] Updated Timer UI to indicate playing or paused state Removed PauseCheck & TimeOfPause from properties, Removed duplicate functionality Resume class , replaced peeking restart at 0 button with persistent play/pause button --- platform/features/clock/bundle.js | 31 +----- .../features/clock/res/templates/timer.html | 2 +- .../clock/src/actions/AbstractTimerAction.js | 19 +++- .../clock/src/actions/PauseTimerAction.js | 19 ++-- .../clock/src/actions/RestartTimerAction.js | 3 +- .../clock/src/actions/ResumeTimerAction.js | 85 --------------- .../clock/src/actions/StartTimerAction.js | 3 +- .../clock/src/actions/StopTimerAction.js | 20 ++-- .../clock/src/controllers/TimerController.js | 35 ++++-- .../test/actions/PauseTimerActionSpec.js | 54 ++++----- .../test/actions/RestartTimerActionSpec.js | 37 +++++-- .../test/actions/ResumeActionTimerSpec.js | 103 ------------------ .../test/actions/StartTimerActionSpec.js | 47 +++++--- .../clock/test/actions/StopTimerActionSpec.js | 47 +++++--- .../test/controllers/TimerControllerSpec.js | 26 ++--- 15 files changed, 201 insertions(+), 330 deletions(-) delete mode 100644 platform/features/clock/src/actions/ResumeTimerAction.js delete mode 100644 platform/features/clock/test/actions/ResumeActionTimerSpec.js diff --git a/platform/features/clock/bundle.js b/platform/features/clock/bundle.js index cf71ec9e38..e2abe6826f 100644 --- a/platform/features/clock/bundle.js +++ b/platform/features/clock/bundle.js @@ -30,7 +30,6 @@ define([ "./src/actions/RestartTimerAction", "./src/actions/StopTimerAction", "./src/actions/PauseTimerAction", - "./src/actions/ResumeTimerAction", "text!./res/templates/clock.html", "text!./res/templates/timer.html", 'legacyRegistry' @@ -44,7 +43,6 @@ define([ RestartTimerAction, StopTimerAction, PauseTimerAction, - ResumeTimerAction, clockTemplate, timerTemplate, legacyRegistry @@ -145,17 +143,6 @@ define([ "cssclass": "icon-play", "priority": "preferred" }, - { - "key": "timer.restart", - "implementation": RestartTimerAction, - "depends": [ - "now" - ], - "category": "contextual", - "name": "Restart at 0", - "cssclass": "icon-refresh", - "priority": "preferred" - }, { "key": "timer.pause", "implementation": PauseTimerAction, @@ -168,14 +155,14 @@ define([ "priority": "preferred" }, { - "key": "timer.resume", - "implementation": ResumeTimerAction, + "key": "timer.restart", + "implementation": RestartTimerAction, "depends": [ "now" ], "category": "contextual", - "name": "Resume", - "cssclass": "icon-play", + "name": "Restart at 0", + "cssclass": "icon-refresh", "priority": "preferred" }, { @@ -277,16 +264,6 @@ define([ "name": "hh:mm:ss" } ] - }, - { - "key": "paused", - "control": "boolean", - "name": "PauseCheck" - }, - { - "key": "pausedTime", - "control": "long", - "name": "TimeOfPause" } ], "model": { diff --git a/platform/features/clock/res/templates/timer.html b/platform/features/clock/res/templates/timer.html index 411012bac1..394184b423 100644 --- a/platform/features/clock/res/templates/timer.html +++ b/platform/features/clock/res/templates/timer.html @@ -23,7 +23,7 @@
+ class="flex-elem s-icon-button {{timer.buttonCssClass()}}"> {{timer.text() || "--:--:--"}} diff --git a/platform/features/clock/src/actions/AbstractTimerAction.js b/platform/features/clock/src/actions/AbstractTimerAction.js index 58de84dfae..07bc7a79fe 100644 --- a/platform/features/clock/src/actions/AbstractTimerAction.js +++ b/platform/features/clock/src/actions/AbstractTimerAction.js @@ -50,15 +50,26 @@ define( now = this.now; function setTimestamp(model) { - model.timestamp = now(); + //if we are resuming + if (model.pausedTime) { + var timeShift = now() - model.pausedTime; + model.timestamp = model.timestamp + timeShift; + } else { + model.timestamp = now(); + } } - function setPaused(model) { - model.paused = false; + function setTimerState(model) { + model.timerState = 'play'; + } + + function setPausedTime(model) { + model.pausedTime = undefined; } return domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setPaused); + domainObject.useCapability('mutation', setTimerState) && + domainObject.useCapability('mutation', setPausedTime); }; return AbstractTimerAction; diff --git a/platform/features/clock/src/actions/PauseTimerAction.js b/platform/features/clock/src/actions/PauseTimerAction.js index efd54830e4..891abbee94 100644 --- a/platform/features/clock/src/actions/PauseTimerAction.js +++ b/platform/features/clock/src/actions/PauseTimerAction.js @@ -25,10 +25,10 @@ define( function (AbstractTimerAction) { /** - * Implements the "Start" action for timers. + * Implements the "Pause" action for timers. * - * Sets the reference timestamp in a timer to the current - * time, such that it begins counting up. + * Sets the reference pausedTime in a timer to the current + * time, such that it stops counting up. * * @extends {platform/features/clock.AbstractTimerAction} * @implements {Action} @@ -51,25 +51,26 @@ define( {}; - // We show this variant for timers which do not yet have - // a target time. + // We show this variant for timers which have + // a target time, or is in a playing state. return model.type === 'timer' && - model.timestamp !== undefined && !model.paused; + (model.timestamp !== undefined || + model.timerState === 'play'); }; PauseTimerAction.prototype.perform = function () { var domainObject = this.domainObject, now = this.now; - function setPaused(model) { - model.paused = true; + function setTimerState(model) { + model.timerState = 'pause'; } function setPausedTime(model) { model.pausedTime = now(); } - return domainObject.useCapability('mutation', setPaused) && + return domainObject.useCapability('mutation', setTimerState) && domainObject.useCapability('mutation', setPausedTime); }; diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js index 6245f1324b..36a6c4417a 100644 --- a/platform/features/clock/src/actions/RestartTimerAction.js +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -53,7 +53,8 @@ define( // We show this variant for timers which already have // a target time. return model.type === 'timer' && - model.timestamp !== undefined; + (model.timestamp !== undefined || + model.timerState !== undefined); }; return RestartTimerAction; diff --git a/platform/features/clock/src/actions/ResumeTimerAction.js b/platform/features/clock/src/actions/ResumeTimerAction.js deleted file mode 100644 index 7e5c160d03..0000000000 --- a/platform/features/clock/src/actions/ResumeTimerAction.js +++ /dev/null @@ -1,85 +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( - ['./AbstractTimerAction'], - function (AbstractTimerAction) { - - /** - * Implements the "Start" action for timers. - * - * Sets the reference timestamp in a timer to the current - * time, such that it begins counting up. - * - * @extends {platform/features/clock.AbstractTimerAction} - * @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 ResumeTimerAction(now, context) { - AbstractTimerAction.apply(this, [now, context]); - } - - ResumeTimerAction.prototype = - Object.create(AbstractTimerAction.prototype); - - ResumeTimerAction.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.timestamp !== undefined && - model.paused; - }; - - ResumeTimerAction.prototype.perform = function () { - var domainObject = this.domainObject, - now = this.now; - - function setPaused(model) { - model.paused = false; - } - - function setTimestamp(model) { - var timeShift = now() - model.pausedTime; - model.timestamp = model.timestamp + timeShift; - } - - function setPausedTime(model) { - model.pausedTime = undefined; - } - - return domainObject.useCapability('mutation', setPaused) && - domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setPausedTime); - }; - - return ResumeTimerAction; - } -); \ No newline at end of file diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index 42abe17247..a29619c02b 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -53,7 +53,8 @@ define( // We show this variant for timers which do not yet have // a target time. return model.type === 'timer' && - model.timestamp === undefined; + (model.timestamp === undefined || + model.timerState !== 'play'); }; return StartTimerAction; diff --git a/platform/features/clock/src/actions/StopTimerAction.js b/platform/features/clock/src/actions/StopTimerAction.js index d3d19b160a..c0d8ed9981 100644 --- a/platform/features/clock/src/actions/StopTimerAction.js +++ b/platform/features/clock/src/actions/StopTimerAction.js @@ -25,10 +25,10 @@ define( function (AbstractTimerAction) { /** - * Implements the "Start" action for timers. + * Implements the "Stop" action for timers. * - * Sets the reference timestamp in a timer to the current - * time, such that it begins counting up. + * Sets the reference timestamp in a timer undefined, + * such that it is reset and makes no movements. * * @extends {platform/features/clock.AbstractTimerAction} * @implements {Action} @@ -54,7 +54,8 @@ define( // We show this variant for timers which do not yet have // a target time. return model.type === 'timer' && - model.timestamp !== undefined; + (model.timestamp !== undefined || + model.timerState !== undefined); }; StopTimerAction.prototype.perform = function () { @@ -64,12 +65,17 @@ define( model.timestamp = undefined; } - function setPaused(model) { - model.paused = false; + function setTimerState(model) { + model.timerState = undefined; + } + + function setPausedTime(model) { + model.pausedTime = undefined; } return domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setPaused); + domainObject.useCapability('mutation', setTimerState) && + domainObject.useCapability('mutation', setPausedTime); }; return StopTimerAction; diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index e2cd2bdb48..0182f00444 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -42,6 +42,7 @@ define( active = true, relativeTimestamp, lastTimestamp, + relativeTimerState, self = this; function update() { @@ -68,24 +69,34 @@ define( relativeTimestamp = timestamp; } + function updateTimerState(timerState) { + relativeTimerState = timerState; + } + + function isPaused() { + return relativeTimerState === 'pause'; + } + function updateObject(domainObject) { var model = domainObject.getModel(), timestamp = model.timestamp, formatKey = model.timerFormat, + timerState = model.timerState, actionCapability = domainObject.getCapability('action'), - actionKey = (timestamp === undefined) ? - 'timer.start' : 'timer.restart'; + actionKey = (timerState !== 'play') ? + 'timer.start' : 'timer.pause'; - self.paused = model.paused; + self.timerState = model.timerState; self.pausedTime = model.pausedTime; - //if paused on startup show last known position - if (self.paused && !lastTimestamp){ - lastTimestamp = self.pausedTime; - } - updateFormat(formatKey); updateTimestamp(timestamp); + updateTimerState(timerState); + + //if paused on startup show last known position + if (isPaused() && !lastTimestamp) { + lastTimestamp = self.pausedTime; + } self.relevantAction = actionCapability && actionCapability.getActions(actionKey)[0]; @@ -107,7 +118,7 @@ define( var lastSign = self.signValue, lastText = self.textValue; - if (!self.paused) { + if (!isPaused()) { lastTimestamp = now(); update(); } @@ -141,7 +152,7 @@ define( /** * Get the CSS class to display the right icon - * for the start/restart button. + * for the start/pause button. * @returns {string} cssclass to display */ TimerController.prototype.buttonCssClass = function () { @@ -150,7 +161,7 @@ define( }; /** - * Get the text to show for the start/restart button + * Get the text to show for the start/pause button * (e.g. in a tooltip) * @returns {string} name of the action */ @@ -161,7 +172,7 @@ define( /** - * Perform the action associated with the start/restart button. + * Perform the action associated with the start/pause button. */ TimerController.prototype.clickButton = function () { if (this.relevantAction) { diff --git a/platform/features/clock/test/actions/PauseTimerActionSpec.js b/platform/features/clock/test/actions/PauseTimerActionSpec.js index 0f8cc1192f..098efa3c64 100644 --- a/platform/features/clock/test/actions/PauseTimerActionSpec.js +++ b/platform/features/clock/test/actions/PauseTimerActionSpec.js @@ -57,7 +57,7 @@ define( }); testModel = {}; - testContext = { domainObject: mockDomainObject }; + testContext = {domainObject: mockDomainObject}; action = new PauseTimerAction(mockNow, testContext); }); @@ -68,36 +68,36 @@ define( expect(testModel.timestamp).toEqual(12000); }); - it("applies only to timers without a target time", function () { - //Timer is on - testModel.type = 'timer'; - testModel.timestamp = 12000; + it("applies only to timers in a playing state", function () { + //in a stopped state + testStates(testModel, 'timer', undefined, undefined, false); - testModel.paused = true; - expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + //in a paused state + testStates(testModel, 'timer', 'pause', undefined, false); - testModel.paused = false; - expect(PauseTimerAction.appliesTo(testContext)).toBeTruthy(); + //in a playing state + testStates(testModel, 'timer', 'play', undefined, true); - //Timer has not started - testModel.timestamp = undefined; - - testModel.paused = true; - expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); - - testModel.paused = false; - expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); - - //Timer is actually a clock - testModel.type = 'clock'; - testModel.timestamp = 12000; - - testModel.paused = true; - expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); - - testModel.paused = false; - expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy(); + //not a timer + testStates(testModel, 'clock', 'pause', undefined, false); }); + + function testStates(testModel, 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() + } + + //first test without time, this test with time + if (timestamp === undefined) { + testStates(testModel, type, timerState, 12000, expected); + } + } }); } ); diff --git a/platform/features/clock/test/actions/RestartTimerActionSpec.js b/platform/features/clock/test/actions/RestartTimerActionSpec.js index c0d4ded90d..96b01196bf 100644 --- a/platform/features/clock/test/actions/RestartTimerActionSpec.js +++ b/platform/features/clock/test/actions/RestartTimerActionSpec.js @@ -68,19 +68,36 @@ define( expect(testModel.timestamp).toEqual(12000); }); - it("applies only to timers with a target time", function () { - testModel.type = 'timer'; - testModel.timestamp = 12000; - expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy(); + it("applies only to timers in a non-stopped state", function () { + //in a stopped state + testStates(testModel, 'timer', undefined, undefined, false); - testModel.type = 'timer'; - testModel.timestamp = undefined; - expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy(); + //in a paused state + testStates(testModel, 'timer', 'pause', undefined, true); - testModel.type = 'clock'; - testModel.timestamp = 12000; - expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy(); + //in a playing state + testStates(testModel, 'timer', 'play', undefined, true); + + //not a timer + testStates(testModel, 'clock', 'pause', undefined, false); }); + + function testStates(testModel, 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() + } + + //first test without time, this test with time + if (timestamp === undefined) { + testStates(testModel, type, timerState, 12000, expected); + } + } }); } ); diff --git a/platform/features/clock/test/actions/ResumeActionTimerSpec.js b/platform/features/clock/test/actions/ResumeActionTimerSpec.js deleted file mode 100644 index 10f6d943dd..0000000000 --- a/platform/features/clock/test/actions/ResumeActionTimerSpec.js +++ /dev/null @@ -1,103 +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/ResumeTimerAction"], - function (ResumeTimerAction) { - - describe("A timer's Resume action", function () { - var mockNow, - mockDomainObject, - testModel, - testContext, - action; - - function asPromise(value) { - return (value || {}).then ? value : { - then: function (callback) { - return asPromise(callback(value)); - } - }; - } - - beforeEach(function () { - mockNow = jasmine.createSpy('now'); - mockDomainObject = jasmine.createSpyObj( - 'domainObject', - ['getCapability', 'useCapability', 'getModel'] - ); - - mockDomainObject.useCapability.andCallFake(function (c, v) { - if (c === 'mutation') { - testModel = v(testModel) || testModel; - return asPromise(true); - } - }); - mockDomainObject.getModel.andCallFake(function () { - return testModel; - }); - - testModel = {}; - testContext = { domainObject: mockDomainObject }; - - action = new ResumeTimerAction(mockNow, testContext); - }); - - it("updates the model with a timestamp", function () { - mockNow.andReturn(12000); - action.perform(); - expect(testModel.timestamp).toEqual(12000); - }); - - it("applies only to timers without a target time", function () { - //Timer is on - testModel.type = 'timer'; - testModel.timestamp = 12000; - - testModel.paused = true; - expect(ResumeTimerAction.appliesTo(testContext)).toBeTruthy(); - - testModel.paused = false; - expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); - - //Timer has not started - testModel.timestamp = undefined; - - testModel.paused = true; - expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); - - testModel.paused = false; - expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); - - //Timer is actually a clock - testModel.type = 'clock'; - testModel.timestamp = 12000; - - testModel.paused = true; - expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); - - testModel.paused = false; - expect(ResumeTimerAction.appliesTo(testContext)).toBeFalsy(); - }); - }); - } -); diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js index 588f776d70..e5952c0d1c 100644 --- a/platform/features/clock/test/actions/StartTimerActionSpec.js +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -33,10 +33,10 @@ define( function asPromise(value) { return (value || {}).then ? value : { - then: function (callback) { - return asPromise(callback(value)); - } - }; + then: function (callback) { + return asPromise(callback(value)); + } + }; } beforeEach(function () { @@ -57,7 +57,7 @@ define( }); testModel = {}; - testContext = { domainObject: mockDomainObject }; + testContext = {domainObject: mockDomainObject}; action = new StartTimerAction(mockNow, testContext); }); @@ -68,19 +68,36 @@ define( expect(testModel.timestamp).toEqual(12000); }); - it("applies only to timers without a target time", function () { - testModel.type = 'timer'; - testModel.timestamp = 12000; - expect(StartTimerAction.appliesTo(testContext)).toBeFalsy(); + it("applies only to timers not in a playing state", function () { + //in a stopped state + testStates(testModel, 'timer', undefined, undefined, true); - testModel.type = 'timer'; - testModel.timestamp = undefined; - expect(StartTimerAction.appliesTo(testContext)).toBeTruthy(); + //in a paused state + testStates(testModel, 'timer', 'pause', undefined, true); - testModel.type = 'clock'; - testModel.timestamp = 12000; - expect(StartTimerAction.appliesTo(testContext)).toBeFalsy(); + //in a playing state + testStates(testModel, 'timer', 'play', undefined, false); + + //not a timer + testStates(testModel, 'clock', 'pause', undefined, false); }); + + function testStates(testModel, 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() + } + + //first test without time, this test with time + if (timestamp === undefined) { + testStates(testModel, type, timerState, 12000, expected); + } + } }); } ); diff --git a/platform/features/clock/test/actions/StopTimerActionSpec.js b/platform/features/clock/test/actions/StopTimerActionSpec.js index 6b86a0bdcf..3f5ef2c910 100644 --- a/platform/features/clock/test/actions/StopTimerActionSpec.js +++ b/platform/features/clock/test/actions/StopTimerActionSpec.js @@ -33,10 +33,10 @@ define( function asPromise(value) { return (value || {}).then ? value : { - then: function (callback) { - return asPromise(callback(value)); - } - }; + then: function (callback) { + return asPromise(callback(value)); + } + }; } beforeEach(function () { @@ -57,7 +57,7 @@ define( }); testModel = {}; - testContext = { domainObject: mockDomainObject }; + testContext = {domainObject: mockDomainObject}; action = new StopTimerAction(mockNow, testContext); }); @@ -68,19 +68,36 @@ define( expect(testModel.timestamp).toEqual(12000); }); - it("applies only to timers without a target time", function () { - testModel.type = 'timer'; - testModel.timestamp = 12000; - expect(StopTimerAction.appliesTo(testContext)).toBeTruthy(); + it("applies only to timers in a non-stopped state", function () { + //in a stopped state + testStates(testModel, 'timer', undefined, undefined, false); - testModel.type = 'timer'; - testModel.timestamp = undefined; - expect(StopTimerAction.appliesTo(testContext)).toBeFalsy(); + //in a paused state + testStates(testModel, 'timer', 'pause', undefined, true); - testModel.type = 'clock'; - testModel.timestamp = 12000; - expect(StopTimerAction.appliesTo(testContext)).toBeFalsy(); + //in a playing state + testStates(testModel, 'timer', 'play', undefined, true); + + //not a timer + testStates(testModel, 'clock', 'pause', undefined, false); }); + + function testStates(testModel, 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() + } + + //first test without time, this test with time + if (timestamp === undefined) { + testStates(testModel, type, timerState, 12000, expected); + } + } }); } ); diff --git a/platform/features/clock/test/controllers/TimerControllerSpec.js b/platform/features/clock/test/controllers/TimerControllerSpec.js index 89fb0a4afc..346286a48b 100644 --- a/platform/features/clock/test/controllers/TimerControllerSpec.js +++ b/platform/features/clock/test/controllers/TimerControllerSpec.js @@ -34,13 +34,13 @@ define( mockDomainObject, mockActionCapability, mockStart, - mockRestart, + mockPause, testModel, controller; function invokeWatch(expr, value) { mockScope.$watch.calls.forEach(function (call) { - if (call.args[0] === expr) { + if (call.args[0] === expr) { call.args[1](value); } }); @@ -67,8 +67,8 @@ define( 'start', ['getMetadata', 'perform'] ); - mockRestart = jasmine.createSpyObj( - 'restart', + mockPause = jasmine.createSpyObj( + 'pause', ['getMetadata', 'perform'] ); mockNow = jasmine.createSpy('now'); @@ -82,11 +82,11 @@ define( mockActionCapability.getActions.andCallFake(function (k) { return [{ 'timer.start': mockStart, - 'timer.restart': mockRestart + 'timer.pause': mockPause }[k]]; }); - mockStart.getMetadata.andReturn({ cssclass: "icon-play", name: "Start" }); - mockRestart.getMetadata.andReturn({ cssclass: "icon-refresh", name: "Restart" }); + mockStart.getMetadata.andReturn({cssclass: "icon-play", name: "Start"}); + mockPause.getMetadata.andReturn({cssclass: "icon-pause", name: "Pause"}); mockScope.domainObject = mockDomainObject; testModel = {}; @@ -144,18 +144,18 @@ define( expect(controller.text()).toEqual("0D 00:00:00"); }); - it("shows cssclass & name for the applicable start/restart action", function () { + 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; invokeWatch('model.modified', 1); - expect(controller.buttonCssClass()).toEqual("icon-refresh"); - expect(controller.buttonText()).toEqual("Restart"); + expect(controller.buttonCssClass()).toEqual("icon-pause"); + expect(controller.buttonText()).toEqual("Pause"); }); - it("performs correct start/restart action on click", function () { + it("performs correct start/pause action on click", function () { invokeWatch('domainObject', mockDomainObject); expect(mockStart.perform).not.toHaveBeenCalled(); controller.clickButton(); @@ -163,9 +163,9 @@ define( testModel.timestamp = 12321; invokeWatch('model.modified', 1); - expect(mockRestart.perform).not.toHaveBeenCalled(); + expect(mockPause.perform).not.toHaveBeenCalled(); controller.clickButton(); - expect(mockRestart.perform).toHaveBeenCalled(); + expect(mockPause.perform).toHaveBeenCalled(); }); it("stops requesting animation frames when destroyed", function () { From 60a8ee657ac823ff35401f15f02de0c6b3278e93 Mon Sep 17 00:00:00 2001 From: DJ Date: Wed, 25 Jan 2017 18:21:52 -0600 Subject: [PATCH 06/12] [Timer] Updated Timer UI and fixed tests Added peeking stop button to view, added legacy and first run support, added new and fixed old tests --- .../features/clock/res/templates/timer.html | 8 +- .../clock/src/actions/AbstractTimerAction.js | 5 +- .../clock/src/actions/PauseTimerAction.js | 5 +- .../clock/src/actions/RestartTimerAction.js | 28 ++++-- .../clock/src/actions/StartTimerAction.js | 3 +- .../clock/src/actions/StopTimerAction.js | 5 +- .../clock/src/controllers/TimerController.js | 86 ++++++++++++++++--- .../test/actions/PauseTimerActionSpec.js | 26 +++--- .../test/actions/RestartTimerActionSpec.js | 44 ++++++---- .../test/actions/StartTimerActionSpec.js | 43 ++++++---- .../clock/test/actions/StopTimerActionSpec.js | 29 ++++--- .../test/controllers/TimerControllerSpec.js | 35 +++++++- 12 files changed, 223 insertions(+), 94 deletions(-) diff --git a/platform/features/clock/res/templates/timer.html b/platform/features/clock/res/templates/timer.html index 394184b423..dea86f1d27 100644 --- a/platform/features/clock/res/templates/timer.html +++ b/platform/features/clock/res/templates/timer.html @@ -22,8 +22,12 @@
+ title="{{timer.buttonText()}}" + class="flex-elem control s-icon-button {{timer.buttonCssClass()}}"> + + {{timer.text() || "--:--:--"}} diff --git a/platform/features/clock/src/actions/AbstractTimerAction.js b/platform/features/clock/src/actions/AbstractTimerAction.js index 07bc7a79fe..8ebfcd0aa6 100644 --- a/platform/features/clock/src/actions/AbstractTimerAction.js +++ b/platform/features/clock/src/actions/AbstractTimerAction.js @@ -30,9 +30,6 @@ define( * Sets the reference timestamp in a timer to the current * time, such that it begins counting up. * - * Both "Start" and "Restart" share this implementation, but - * control their visibility with different `appliesTo` behavior. - * * @implements {Action} * @memberof platform/features/clock * @constructor @@ -60,7 +57,7 @@ define( } function setTimerState(model) { - model.timerState = 'play'; + model.timerState = 'started'; } function setPausedTime(model) { diff --git a/platform/features/clock/src/actions/PauseTimerAction.js b/platform/features/clock/src/actions/PauseTimerAction.js index 891abbee94..5156c08a13 100644 --- a/platform/features/clock/src/actions/PauseTimerAction.js +++ b/platform/features/clock/src/actions/PauseTimerAction.js @@ -54,8 +54,7 @@ define( // We show this variant for timers which have // a target time, or is in a playing state. return model.type === 'timer' && - (model.timestamp !== undefined || - model.timerState === 'play'); + model.timerState === 'started'; }; PauseTimerAction.prototype.perform = function () { @@ -63,7 +62,7 @@ define( now = this.now; function setTimerState(model) { - model.timerState = 'pause'; + model.timerState = 'paused'; } function setPausedTime(model) { diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js index 36a6c4417a..27032ce22a 100644 --- a/platform/features/clock/src/actions/RestartTimerAction.js +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -50,14 +50,32 @@ define( (context.domainObject && context.domainObject.getModel()) || {}; - // We show this variant for timers which already have - // a target time. + // We show this variant for timers which already have a target time. return model.type === 'timer' && - (model.timestamp !== undefined || - model.timerState !== undefined); + model.timerState !== 'stopped'; + }; + + RestartTimerAction.prototype.perform = function () { + var domainObject = this.domainObject, + now = this.now; + + function setTimestamp(model) { + model.timestamp = now(); + } + + function setTimerState(model) { + model.timerState = 'started'; + } + + function setPausedTime(model) { + model.pausedTime = undefined; + } + + return domainObject.useCapability('mutation', setTimestamp) && + domainObject.useCapability('mutation', setTimerState) && + domainObject.useCapability('mutation', setPausedTime); }; return RestartTimerAction; - } ); diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index a29619c02b..5b7211666a 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -53,8 +53,7 @@ define( // We show this variant for timers which do not yet have // a target time. return model.type === 'timer' && - (model.timestamp === undefined || - model.timerState !== 'play'); + model.timerState !== 'started'; }; return StartTimerAction; diff --git a/platform/features/clock/src/actions/StopTimerAction.js b/platform/features/clock/src/actions/StopTimerAction.js index c0d8ed9981..88a213b934 100644 --- a/platform/features/clock/src/actions/StopTimerAction.js +++ b/platform/features/clock/src/actions/StopTimerAction.js @@ -54,8 +54,7 @@ define( // We show this variant for timers which do not yet have // a target time. return model.type === 'timer' && - (model.timestamp !== undefined || - model.timerState !== undefined); + model.timerState !== 'stopped'; }; StopTimerAction.prototype.perform = function () { @@ -66,7 +65,7 @@ define( } function setTimerState(model) { - model.timerState = undefined; + model.timerState = 'stopped'; } function setPausedTime(model) { diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index 0182f00444..5252f67f00 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -54,10 +54,13 @@ define( timeDelta >= 1000 ? "+" : ""; self.signCssClass = timeDelta < 0 ? "icon-minus" : timeDelta >= 1000 ? "icon-plus" : ""; + self.stateCssClass = relativeTimerState === "play" ? "icon-play" : + relativeTimerState === "pause" ? "icon-pause" : "icon-box"; } else { self.textValue = ""; self.signValue = ""; self.signCssClass = ""; + self.stateCssClass = "icon-box"; } } @@ -73,34 +76,47 @@ define( 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 === 'pause'; + return relativeTimerState === 'paused'; + } + + function handleLegacyTimer(model) { + if (model.timerState === undefined) { + model.timerState = model.timestamp === undefined ? + 'stopped' : 'started'; + } } function updateObject(domainObject) { - var model = domainObject.getModel(), - timestamp = model.timestamp, + var model = domainObject.getModel(); + handleLegacyTimer(model); + + var timestamp = model.timestamp, formatKey = model.timerFormat, timerState = model.timerState, actionCapability = domainObject.getCapability('action'), - actionKey = (timerState !== 'play') ? + actionKey = (timerState !== 'started') ? 'timer.start' : 'timer.pause'; - self.timerState = model.timerState; - self.pausedTime = model.pausedTime; - updateFormat(formatKey); updateTimestamp(timestamp); updateTimerState(timerState); + updateActions(actionCapability, actionKey); //if paused on startup show last known position if (isPaused() && !lastTimestamp) { - lastTimestamp = self.pausedTime; + lastTimestamp = model.pausedTime; } - self.relevantAction = actionCapability && - actionCapability.getActions(actionKey)[0]; - update(); } @@ -122,6 +138,11 @@ define( 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. @@ -157,7 +178,7 @@ define( */ TimerController.prototype.buttonCssClass = function () { return this.relevantAction ? - this.relevantAction.getMetadata().cssclass : ""; + this.relevantAction.getMetadata().cssclass : ""; }; /** @@ -167,7 +188,7 @@ define( */ TimerController.prototype.buttonText = function () { return this.relevantAction ? - this.relevantAction.getMetadata().name : ""; + this.relevantAction.getMetadata().name : ""; }; @@ -181,6 +202,36 @@ define( } }; + /** + * Get the CSS class to display the stop button + * @returns {string} cssclass to display + */ + TimerController.prototype.stopButtonCssClass = function () { + return this.stopAction ? + this.stopAction.getMetadata().cssclass : ''; + }; + + /** + * Get the text to show the stop button + * (e.g. in a tooltip) + * @returns {string} name of the action + */ + TimerController.prototype.stopButtonText = function () { + return this.stopAction ? + this.stopAction.getMetadata().name : ''; + }; + + + /** + * 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. @@ -199,6 +250,15 @@ define( return this.signCssClass; }; + /** + * Get the symbol (play, pause or stop) of the current timer state, as + * a CSS class. + * @returns {string} symbol of the current timer state + */ + TimerController.prototype.stateClass = function () { + return this.stateCssClass; + }; + /** * Get the text to display for the current timer value. * @returns {string} current timer value diff --git a/platform/features/clock/test/actions/PauseTimerActionSpec.js b/platform/features/clock/test/actions/PauseTimerActionSpec.js index 098efa3c64..7cf230968f 100644 --- a/platform/features/clock/test/actions/PauseTimerActionSpec.js +++ b/platform/features/clock/test/actions/PauseTimerActionSpec.js @@ -62,27 +62,34 @@ define( action = new PauseTimerAction(mockNow, testContext); }); - it("updates the model with a timestamp", function () { + 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.andReturn(12000); action.perform(); - expect(testModel.timestamp).toEqual(12000); + expect(testModel.pausedTime).toEqual(12000); }); it("applies only to timers in a playing state", function () { //in a stopped state - testStates(testModel, 'timer', undefined, undefined, false); + testState('timer', 'stopped', undefined, false); //in a paused state - testStates(testModel, 'timer', 'pause', undefined, false); + testState('timer', 'paused', 12000, false); //in a playing state - testStates(testModel, 'timer', 'play', undefined, true); + testState('timer', 'started', 12000, true); //not a timer - testStates(testModel, 'clock', 'pause', undefined, false); + testState('clock', 'started', 12000, false); }); - function testStates(testModel, type, timerState, timestamp, expected) { + function testState(type, timerState, timestamp, expected) { testModel.type = type; testModel.timerState = timerState; testModel.timestamp = timestamp; @@ -92,11 +99,6 @@ define( } else { expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy() } - - //first test without time, this test with time - if (timestamp === undefined) { - testStates(testModel, type, timerState, 12000, expected); - } } }); } diff --git a/platform/features/clock/test/actions/RestartTimerActionSpec.js b/platform/features/clock/test/actions/RestartTimerActionSpec.js index 96b01196bf..e1bddbe4eb 100644 --- a/platform/features/clock/test/actions/RestartTimerActionSpec.js +++ b/platform/features/clock/test/actions/RestartTimerActionSpec.js @@ -63,26 +63,39 @@ define( }); it("updates the model with a timestamp", function () { + testModel.pausedTime = 12000; mockNow.andReturn(12000); action.perform(); expect(testModel.timestamp).toEqual(12000); }); - it("applies only to timers in a non-stopped state", function () { - //in a stopped state - testStates(testModel, 'timer', undefined, undefined, false); - - //in a paused state - testStates(testModel, 'timer', 'pause', undefined, true); - - //in a playing state - testStates(testModel, 'timer', 'play', undefined, true); - - //not a timer - testStates(testModel, 'clock', 'pause', undefined, false); + it("updates the model with a pausedTime", function () { + testModel.pausedTime = 12000; + action.perform(); + expect(testModel.pausedTime).toEqual(undefined); }); - function testStates(testModel, type, timerState, timestamp, expected) { + 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); + }); + + function testState(type, timerState, timestamp, expected) { testModel.type = type; testModel.timerState = timerState; testModel.timestamp = timestamp; @@ -92,11 +105,6 @@ define( } else { expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy() } - - //first test without time, this test with time - if (timestamp === undefined) { - testStates(testModel, type, timerState, 12000, expected); - } } }); } diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js index e5952c0d1c..a7a34f2043 100644 --- a/platform/features/clock/test/actions/StartTimerActionSpec.js +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -68,21 +68,33 @@ define( expect(testModel.timestamp).toEqual(12000); }); - it("applies only to timers not in a playing state", function () { - //in a stopped state - testStates(testModel, 'timer', undefined, undefined, true); - - //in a paused state - testStates(testModel, 'timer', 'pause', undefined, true); - - //in a playing state - testStates(testModel, 'timer', 'play', undefined, false); - - //not a timer - testStates(testModel, 'clock', 'pause', undefined, false); + it("updates the model with a pausedTime", function () { + testModel.pausedTime = 12000; + action.perform(); + expect(testModel.pausedTime).toEqual(undefined); }); - function testStates(testModel, type, timerState, timestamp, expected) { + 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); + }); + + function testState(type, timerState, timestamp, expected) { testModel.type = type; testModel.timerState = timerState; testModel.timestamp = timestamp; @@ -92,11 +104,6 @@ define( } else { expect(StartTimerAction.appliesTo(testContext)).toBeFalsy() } - - //first test without time, this test with time - if (timestamp === undefined) { - testStates(testModel, type, timerState, 12000, expected); - } } }); } diff --git a/platform/features/clock/test/actions/StopTimerActionSpec.js b/platform/features/clock/test/actions/StopTimerActionSpec.js index 3f5ef2c910..e54d7ca514 100644 --- a/platform/features/clock/test/actions/StopTimerActionSpec.js +++ b/platform/features/clock/test/actions/StopTimerActionSpec.js @@ -65,24 +65,36 @@ define( it("updates the model with a timestamp", function () { mockNow.andReturn(12000); action.perform(); - expect(testModel.timestamp).toEqual(12000); + 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 - testStates(testModel, 'timer', undefined, undefined, false); + testState('timer', 'stopped', undefined, false); //in a paused state - testStates(testModel, 'timer', 'pause', undefined, true); + testState('timer', 'paused', 12000, true); //in a playing state - testStates(testModel, 'timer', 'play', undefined, true); + testState('timer', 'started', 12000, true); //not a timer - testStates(testModel, 'clock', 'pause', undefined, false); + testState('clock', 'paused', 12000, false); }); - function testStates(testModel, type, timerState, timestamp, expected) { + function testState(type, timerState, timestamp, expected) { testModel.type = type; testModel.timerState = timerState; testModel.timestamp = timestamp; @@ -92,11 +104,6 @@ define( } else { expect(StopTimerAction.appliesTo(testContext)).toBeFalsy() } - - //first test without time, this test with time - if (timestamp === undefined) { - testStates(testModel, type, timerState, 12000, expected); - } } }); } diff --git a/platform/features/clock/test/controllers/TimerControllerSpec.js b/platform/features/clock/test/controllers/TimerControllerSpec.js index 346286a48b..486f158111 100644 --- a/platform/features/clock/test/controllers/TimerControllerSpec.js +++ b/platform/features/clock/test/controllers/TimerControllerSpec.js @@ -35,6 +35,7 @@ define( mockActionCapability, mockStart, mockPause, + mockStop, testModel, controller; @@ -68,7 +69,11 @@ define( ['getMetadata', 'perform'] ); mockPause = jasmine.createSpyObj( - 'pause', + 'paused', + ['getMetadata', 'perform'] + ); + mockStop = jasmine.createSpyObj( + 'stopped', ['getMetadata', 'perform'] ); mockNow = jasmine.createSpy('now'); @@ -82,11 +87,13 @@ define( mockActionCapability.getActions.andCallFake(function (k) { return [{ 'timer.start': mockStart, - 'timer.pause': mockPause + 'timer.pause': mockPause, + 'timer.stop': mockStop }[k]]; }); mockStart.getMetadata.andReturn({cssclass: "icon-play", name: "Start"}); mockPause.getMetadata.andReturn({cssclass: "icon-pause", name: "Pause"}); + mockStop.getMetadata.andReturn({cssclass: "icon-box", name: "Stop"}); mockScope.domainObject = mockDomainObject; testModel = {}; @@ -120,6 +127,7 @@ define( mockWindow.requestAnimationFrame.mostRecentCall.args[0](); expect(controller.sign()).toEqual(""); expect(controller.text()).toEqual(""); + expect(controller.stopButtonText()).toEqual(""); }); it("formats time to display relative to target", function () { @@ -150,22 +158,43 @@ define( 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 action on click", function () { + it("shows cssclass & name for the stop action", function () { + invokeWatch('domainObject', mockDomainObject); + expect(controller.stopButtonCssClass()).toEqual(""); + expect(controller.stopButtonText()).toEqual(""); + + testModel.timestamp = 12321; + testModel.timerState = 'started'; + invokeWatch('model.modified', 1); + expect(controller.stopButtonCssClass()).toEqual("icon-box"); + expect(controller.stopButtonText()).toEqual("Stop"); + }); + + 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 () { From 4072b91808a67c8d602c6b498a61048365a0ad33 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 26 Jan 2017 11:57:08 -0800 Subject: [PATCH 07/12] [Frontend] Styling for Timer Fixes #623 Mod to code to allow timerState to be accessible to markup; CSS for stop button and paused/stopped states --- .../res/sass/features/_time-display.scss | 53 +++++++++++++++---- .../features/clock/res/templates/timer.html | 17 +++--- .../clock/src/controllers/TimerController.js | 2 +- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/platform/commonUI/general/res/sass/features/_time-display.scss b/platform/commonUI/general/res/sass/features/_time-display.scss index 94f64f9bb7..8c019860ad 100644 --- a/platform/commonUI/general/res/sass/features/_time-display.scss +++ b/platform/commonUI/general/res/sass/features/_time-display.scss @@ -1,33 +1,36 @@ .l-time-display { $transTime: 200ms; + $controlSize: 14px; + $control1ControlW: $controlSize + $interiorMargin; + $control2ControlW: $control1ControlW * 2; line-height: 140%; &:hover { - .l-btn.control { + .l-btn.controls { opacity: 1; } } &.l-timer { - .l-value:before, - .control { - font-size: 0.8em; - } - .l-value:before { // Direction +/- element + font-size: $controlSize; margin-right: $interiorMarginSm; + } - .control { + .controls { @include trans-prop-nice((width, opacity), $transTime); + font-size: $controlSize; line-height: inherit; margin-right: 0; opacity: 0; width: 0; + .flex-elem { + margin-right: $interiorMargin; + } } - &:hover .control { - margin-right: $interiorMargin; + &:hover .controls { opacity: 1; - width: 1em; + width: $control2ControlW; } } @@ -35,4 +38,34 @@ color: pullForward($colorBodyFg, 50%); font-weight: 400; } + + // States + &.s-state-stopped, + &.s-state-paused { + .l-value { + opacity: 0.4; + } + } + + &.s-state-started { + .l-value { + opacity: 1; + } + } + + &.s-state-stopped { + // Hide Stop button, 1controlW + .t-btn-stop { + display: none; + } + &:hover .controls { width: $control1ControlW; } + + } + + &.s-state-paused { + // Paused, do something visual + .l-value { + &:before { @extend .pulse; } + } + } } diff --git a/platform/features/clock/res/templates/timer.html b/platform/features/clock/res/templates/timer.html index dea86f1d27..55f36398de 100644 --- a/platform/features/clock/res/templates/timer.html +++ b/platform/features/clock/res/templates/timer.html @@ -19,15 +19,16 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> -
+
- - - +
+ + +
{{timer.text() || "--:--:--"}} diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index 5252f67f00..e08e8b6daa 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -73,7 +73,7 @@ define( } function updateTimerState(timerState) { - relativeTimerState = timerState; + self.timerState = relativeTimerState = timerState; } function updateActions(actionCapability, actionKey) { From 6459f410e744a515d4b1324bcbb0ce257b589597 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 26 Jan 2017 15:09:01 -0800 Subject: [PATCH 08/12] [Frontend] Code style cleanups in Timer *spec files Fixes #623 Moved testState() function placement in code; Added semicolons; --- .../test/actions/PauseTimerActionSpec.js | 24 +++++++++---------- .../test/actions/RestartTimerActionSpec.js | 24 +++++++++---------- .../test/actions/StartTimerActionSpec.js | 24 +++++++++---------- .../clock/test/actions/StopTimerActionSpec.js | 24 +++++++++---------- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/platform/features/clock/test/actions/PauseTimerActionSpec.js b/platform/features/clock/test/actions/PauseTimerActionSpec.js index 7cf230968f..098e29c634 100644 --- a/platform/features/clock/test/actions/PauseTimerActionSpec.js +++ b/platform/features/clock/test/actions/PauseTimerActionSpec.js @@ -39,6 +39,18 @@ define( }; } + 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( @@ -88,18 +100,6 @@ define( //not a timer testState('clock', 'started', 12000, false); }); - - 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() - } - } }); } ); diff --git a/platform/features/clock/test/actions/RestartTimerActionSpec.js b/platform/features/clock/test/actions/RestartTimerActionSpec.js index e1bddbe4eb..fc1978322f 100644 --- a/platform/features/clock/test/actions/RestartTimerActionSpec.js +++ b/platform/features/clock/test/actions/RestartTimerActionSpec.js @@ -39,6 +39,18 @@ define( }; } + 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( @@ -94,18 +106,6 @@ define( //not a timer testState('clock', 'paused', 12000, false); }); - - 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() - } - } }); } ); diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js index a7a34f2043..1506c79738 100644 --- a/platform/features/clock/test/actions/StartTimerActionSpec.js +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -39,6 +39,18 @@ define( }; } + 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( @@ -93,18 +105,6 @@ define( //not a timer testState('clock', 'paused', 12000, false); }); - - 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() - } - } }); } ); diff --git a/platform/features/clock/test/actions/StopTimerActionSpec.js b/platform/features/clock/test/actions/StopTimerActionSpec.js index e54d7ca514..a4c27ad921 100644 --- a/platform/features/clock/test/actions/StopTimerActionSpec.js +++ b/platform/features/clock/test/actions/StopTimerActionSpec.js @@ -39,6 +39,18 @@ define( }; } + 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( @@ -93,18 +105,6 @@ define( //not a timer testState('clock', 'paused', 12000, false); }); - - 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() - } - } }); } ); From 26db493b0d9806cf6bc28ae3b433273d73592c64 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 26 Jan 2017 15:27:05 -0800 Subject: [PATCH 09/12] [Frontend] Removed unused var Fixes #623 Removed stateCssClass, not needed; --- .../clock/src/controllers/TimerController.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index e08e8b6daa..974c4eade7 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -54,13 +54,10 @@ define( timeDelta >= 1000 ? "+" : ""; self.signCssClass = timeDelta < 0 ? "icon-minus" : timeDelta >= 1000 ? "icon-plus" : ""; - self.stateCssClass = relativeTimerState === "play" ? "icon-play" : - relativeTimerState === "pause" ? "icon-pause" : "icon-box"; } else { self.textValue = ""; self.signValue = ""; self.signCssClass = ""; - self.stateCssClass = "icon-box"; } } @@ -250,15 +247,6 @@ define( return this.signCssClass; }; - /** - * Get the symbol (play, pause or stop) of the current timer state, as - * a CSS class. - * @returns {string} symbol of the current timer state - */ - TimerController.prototype.stateClass = function () { - return this.stateCssClass; - }; - /** * Get the text to display for the current timer value. * @returns {string} current timer value From 5aa93ba50c61258b294cceff3ca37450c2b26f75 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 9 Apr 2017 18:40:22 -0500 Subject: [PATCH 10/12] [Timer] Back-end code cleanup and removed unused code Cleaned up code by removing unused and unneeded code and the tests associated with it --- .../clock/src/actions/AbstractTimerAction.js | 74 ------------------ .../clock/src/actions/PauseTimerAction.js | 11 +-- .../clock/src/actions/RestartTimerAction.js | 11 +-- .../clock/src/actions/StartTimerAction.js | 38 ++++++++-- .../clock/src/actions/StopTimerAction.js | 11 +-- .../clock/src/controllers/TimerController.js | 32 -------- .../test/actions/AbstractTimerActionSpec.js | 75 ------------------- .../test/controllers/TimerControllerSpec.js | 13 ---- 8 files changed, 43 insertions(+), 222 deletions(-) delete mode 100644 platform/features/clock/src/actions/AbstractTimerAction.js delete mode 100644 platform/features/clock/test/actions/AbstractTimerActionSpec.js diff --git a/platform/features/clock/src/actions/AbstractTimerAction.js b/platform/features/clock/src/actions/AbstractTimerAction.js deleted file mode 100644 index 8ebfcd0aa6..0000000000 --- a/platform/features/clock/src/actions/AbstractTimerAction.js +++ /dev/null @@ -1,74 +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" and "Restart" 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 AbstractTimerAction(now, context) { - this.domainObject = context.domainObject; - this.now = now; - } - - AbstractTimerAction.prototype.perform = function () { - var domainObject = this.domainObject, - now = this.now; - - function setTimestamp(model) { - //if we are resuming - if (model.pausedTime) { - var timeShift = now() - model.pausedTime; - model.timestamp = model.timestamp + timeShift; - } else { - model.timestamp = now(); - } - } - - function setTimerState(model) { - model.timerState = 'started'; - } - - function setPausedTime(model) { - model.pausedTime = undefined; - } - - return domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setTimerState) && - domainObject.useCapability('mutation', setPausedTime); - }; - - return AbstractTimerAction; - } -); diff --git a/platform/features/clock/src/actions/PauseTimerAction.js b/platform/features/clock/src/actions/PauseTimerAction.js index 5156c08a13..0d94e07a21 100644 --- a/platform/features/clock/src/actions/PauseTimerAction.js +++ b/platform/features/clock/src/actions/PauseTimerAction.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ['./AbstractTimerAction'], - function (AbstractTimerAction) { + [], + function () { /** * Implements the "Pause" action for timers. @@ -30,7 +30,6 @@ define( * Sets the reference pausedTime in a timer to the current * time, such that it stops counting up. * - * @extends {platform/features/clock.AbstractTimerAction} * @implements {Action} * @memberof platform/features/clock * @constructor @@ -39,12 +38,10 @@ define( * @param {ActionContext} context the context for this action */ function PauseTimerAction(now, context) { - AbstractTimerAction.apply(this, [now, context]); + this.domainObject = context.domainObject; + this.now = now; } - PauseTimerAction.prototype = - Object.create(AbstractTimerAction.prototype); - PauseTimerAction.appliesTo = function (context) { var model = (context.domainObject && context.domainObject.getModel()) || diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js index 27032ce22a..4197b0c082 100644 --- a/platform/features/clock/src/actions/RestartTimerAction.js +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ['./AbstractTimerAction'], - function (AbstractTimerAction) { + [], + function () { /** * Implements the "Restart at 0" action. @@ -30,7 +30,6 @@ define( * Behaves the same as (and delegates functionality to) * the "Start" action. * - * @extends {platform/features/clock.AbstractTimerAction} * @implements {Action} * @memberof platform/features/clock * @constructor @@ -39,12 +38,10 @@ define( * @param {ActionContext} context the context for this action */ function RestartTimerAction(now, context) { - AbstractTimerAction.apply(this, [now, context]); + this.domainObject = context.domainObject; + this.now = now; } - RestartTimerAction.prototype = - Object.create(AbstractTimerAction.prototype); - RestartTimerAction.appliesTo = function (context) { var model = (context.domainObject && context.domainObject.getModel()) || diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index 5b7211666a..c1b49c805b 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ['./AbstractTimerAction'], - function (AbstractTimerAction) { + [], + function () { /** * Implements the "Start" action for timers. @@ -30,7 +30,6 @@ define( * Sets the reference timestamp in a timer to the current * time, such that it begins counting up. * - * @extends {platform/features/clock.AbstractTimerAction} * @implements {Action} * @memberof platform/features/clock * @constructor @@ -39,12 +38,10 @@ define( * @param {ActionContext} context the context for this action */ function StartTimerAction(now, context) { - AbstractTimerAction.apply(this, [now, context]); + this.domainObject = context.domainObject; + this.now = now; } - StartTimerAction.prototype = - Object.create(AbstractTimerAction.prototype); - StartTimerAction.appliesTo = function (context) { var model = (context.domainObject && context.domainObject.getModel()) || @@ -56,6 +53,33 @@ define( model.timerState !== 'started'; }; + StartTimerAction.prototype.perform = function () { + var domainObject = this.domainObject, + now = this.now; + + function setTimestamp(model) { + //if we are resuming + if (model.pausedTime) { + var timeShift = now() - model.pausedTime; + model.timestamp = model.timestamp + timeShift; + } else { + model.timestamp = now(); + } + } + + function setTimerState(model) { + model.timerState = 'started'; + } + + function setPausedTime(model) { + model.pausedTime = undefined; + } + + return domainObject.useCapability('mutation', setTimestamp) && + domainObject.useCapability('mutation', setTimerState) && + domainObject.useCapability('mutation', setPausedTime); + }; + return StartTimerAction; } ); diff --git a/platform/features/clock/src/actions/StopTimerAction.js b/platform/features/clock/src/actions/StopTimerAction.js index 88a213b934..f91fcde7bf 100644 --- a/platform/features/clock/src/actions/StopTimerAction.js +++ b/platform/features/clock/src/actions/StopTimerAction.js @@ -21,8 +21,8 @@ *****************************************************************************/ define( - ['./AbstractTimerAction'], - function (AbstractTimerAction) { + [], + function () { /** * Implements the "Stop" action for timers. @@ -30,7 +30,6 @@ define( * Sets the reference timestamp in a timer undefined, * such that it is reset and makes no movements. * - * @extends {platform/features/clock.AbstractTimerAction} * @implements {Action} * @memberof platform/features/clock * @constructor @@ -39,12 +38,10 @@ define( * @param {ActionContext} context the context for this action */ function StopTimerAction(now, context) { - AbstractTimerAction.apply(this, [now, context]); + this.domainObject = context.domainObject; + this.now = now; } - StopTimerAction.prototype = - Object.create(AbstractTimerAction.prototype); - StopTimerAction.appliesTo = function (context) { var model = (context.domainObject && context.domainObject.getModel()) || diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index 974c4eade7..b4cd63c0ba 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -52,12 +52,9 @@ define( 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 = ""; } } @@ -199,26 +196,6 @@ define( } }; - /** - * Get the CSS class to display the stop button - * @returns {string} cssclass to display - */ - TimerController.prototype.stopButtonCssClass = function () { - return this.stopAction ? - this.stopAction.getMetadata().cssclass : ''; - }; - - /** - * Get the text to show the stop button - * (e.g. in a tooltip) - * @returns {string} name of the action - */ - TimerController.prototype.stopButtonText = function () { - return this.stopAction ? - this.stopAction.getMetadata().name : ''; - }; - - /** * Perform the action associated with the stop button. */ @@ -238,15 +215,6 @@ define( 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 diff --git a/platform/features/clock/test/actions/AbstractTimerActionSpec.js b/platform/features/clock/test/actions/AbstractTimerActionSpec.js deleted file mode 100644 index 74d8763ced..0000000000 --- a/platform/features/clock/test/actions/AbstractTimerActionSpec.js +++ /dev/null @@ -1,75 +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/AbstractTimerAction"], - function (AbstractTimerAction) { - - describe("A timer's start/restart action", function () { - var mockNow, - mockDomainObject, - testModel, - action; - - function asPromise(value) { - return (value || {}).then ? value : { - then: function (callback) { - return asPromise(callback(value)); - } - }; - } - - beforeEach(function () { - mockNow = jasmine.createSpy('now'); - mockDomainObject = jasmine.createSpyObj( - 'domainObject', - ['getCapability', 'useCapability'] - ); - - mockDomainObject.useCapability.andCallFake(function (c, v) { - if (c === 'mutation') { - testModel = v(testModel) || testModel; - return asPromise(true); - } - }); - - testModel = {}; - - action = new AbstractTimerAction(mockNow, { - domainObject: mockDomainObject - }); - }); - - it("updates the model with a timestamp", function () { - mockNow.andReturn(12000); - action.perform(); - expect(testModel.timestamp).toEqual(12000); - }); - - it("does not truncate milliseconds", function () { - mockNow.andReturn(42321); - action.perform(); - expect(testModel.timestamp).toEqual(42321); - }); - }); - } -); diff --git a/platform/features/clock/test/controllers/TimerControllerSpec.js b/platform/features/clock/test/controllers/TimerControllerSpec.js index 486f158111..b912dc8921 100644 --- a/platform/features/clock/test/controllers/TimerControllerSpec.js +++ b/platform/features/clock/test/controllers/TimerControllerSpec.js @@ -127,7 +127,6 @@ define( mockWindow.requestAnimationFrame.mostRecentCall.args[0](); expect(controller.sign()).toEqual(""); expect(controller.text()).toEqual(""); - expect(controller.stopButtonText()).toEqual(""); }); it("formats time to display relative to target", function () { @@ -164,18 +163,6 @@ define( expect(controller.buttonText()).toEqual("Pause"); }); - it("shows cssclass & name for the stop action", function () { - invokeWatch('domainObject', mockDomainObject); - expect(controller.stopButtonCssClass()).toEqual(""); - expect(controller.stopButtonText()).toEqual(""); - - testModel.timestamp = 12321; - testModel.timerState = 'started'; - invokeWatch('model.modified', 1); - expect(controller.stopButtonCssClass()).toEqual("icon-box"); - expect(controller.stopButtonText()).toEqual("Stop"); - }); - it("performs correct start/pause/stop action on click", function () { //test start invokeWatch('domainObject', mockDomainObject); From 6e1a43130d3fc369706e31704580d5d894a14e6d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 3 May 2017 16:15:17 -0700 Subject: [PATCH 11/12] [Timers] Simplify mutation calls --- .../features/clock/src/actions/PauseTimerAction.js | 8 ++------ .../features/clock/src/actions/RestartTimerAction.js | 12 ++---------- .../features/clock/src/actions/StartTimerAction.js | 12 ++---------- .../features/clock/src/actions/StopTimerAction.js | 12 ++---------- 4 files changed, 8 insertions(+), 36 deletions(-) diff --git a/platform/features/clock/src/actions/PauseTimerAction.js b/platform/features/clock/src/actions/PauseTimerAction.js index 0d94e07a21..f3e5910bef 100644 --- a/platform/features/clock/src/actions/PauseTimerAction.js +++ b/platform/features/clock/src/actions/PauseTimerAction.js @@ -58,16 +58,12 @@ define( var domainObject = this.domainObject, now = this.now; - function setTimerState(model) { + function updateModel(model) { model.timerState = 'paused'; - } - - function setPausedTime(model) { model.pausedTime = now(); } - return domainObject.useCapability('mutation', setTimerState) && - domainObject.useCapability('mutation', setPausedTime); + return domainObject.useCapability('mutation', updateModel); }; return PauseTimerAction; diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js index 4197b0c082..dad88dad3a 100644 --- a/platform/features/clock/src/actions/RestartTimerAction.js +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -56,21 +56,13 @@ define( var domainObject = this.domainObject, now = this.now; - function setTimestamp(model) { + function updateModel(model) { model.timestamp = now(); - } - - function setTimerState(model) { model.timerState = 'started'; - } - - function setPausedTime(model) { model.pausedTime = undefined; } - return domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setTimerState) && - domainObject.useCapability('mutation', setPausedTime); + return domainObject.useCapability('mutation', updateModel); }; return RestartTimerAction; diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index c1b49c805b..a88170db57 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -57,7 +57,7 @@ define( var domainObject = this.domainObject, now = this.now; - function setTimestamp(model) { + function updateModel(model) { //if we are resuming if (model.pausedTime) { var timeShift = now() - model.pausedTime; @@ -65,19 +65,11 @@ define( } else { model.timestamp = now(); } - } - - function setTimerState(model) { model.timerState = 'started'; - } - - function setPausedTime(model) { model.pausedTime = undefined; } - return domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setTimerState) && - domainObject.useCapability('mutation', setPausedTime); + return domainObject.useCapability('mutation', updateModel); }; return StartTimerAction; diff --git a/platform/features/clock/src/actions/StopTimerAction.js b/platform/features/clock/src/actions/StopTimerAction.js index f91fcde7bf..806e27c746 100644 --- a/platform/features/clock/src/actions/StopTimerAction.js +++ b/platform/features/clock/src/actions/StopTimerAction.js @@ -57,21 +57,13 @@ define( StopTimerAction.prototype.perform = function () { var domainObject = this.domainObject; - function setTimestamp(model) { + function updateModel(model) { model.timestamp = undefined; - } - - function setTimerState(model) { model.timerState = 'stopped'; - } - - function setPausedTime(model) { model.pausedTime = undefined; } - return domainObject.useCapability('mutation', setTimestamp) && - domainObject.useCapability('mutation', setTimerState) && - domainObject.useCapability('mutation', setPausedTime); + return domainObject.useCapability('mutation', updateModel); }; return StopTimerAction; From f8a04d0fc2b3e31cbee1d8620dc4fcac1856f4d0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 3 May 2017 16:30:06 -0700 Subject: [PATCH 12/12] [Timer] cssclass to cssClass Along with preceding changes, fixes #623 --- platform/features/clock/bundle.js | 4 ++-- .../features/clock/src/controllers/TimerController.js | 2 +- .../clock/test/controllers/TimerControllerSpec.js | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/platform/features/clock/bundle.js b/platform/features/clock/bundle.js index 1078bcb1bd..022f81f809 100644 --- a/platform/features/clock/bundle.js +++ b/platform/features/clock/bundle.js @@ -151,7 +151,7 @@ define([ ], "category": "contextual", "name": "Pause", - "cssclass": "icon-pause", + "cssClass": "icon-pause", "priority": "preferred" }, { @@ -173,7 +173,7 @@ define([ ], "category": "contextual", "name": "Stop", - "cssclass": "icon-box", + "cssClass": "icon-box", "priority": "preferred" } ], diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index b4cd63c0ba..5c92ee4991 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -172,7 +172,7 @@ define( */ TimerController.prototype.buttonCssClass = function () { return this.relevantAction ? - this.relevantAction.getMetadata().cssclass : ""; + this.relevantAction.getMetadata().cssClass : ""; }; /** diff --git a/platform/features/clock/test/controllers/TimerControllerSpec.js b/platform/features/clock/test/controllers/TimerControllerSpec.js index 8a5ad70113..002ce5c007 100644 --- a/platform/features/clock/test/controllers/TimerControllerSpec.js +++ b/platform/features/clock/test/controllers/TimerControllerSpec.js @@ -92,9 +92,9 @@ define( }[k]]; }); - mockStart.getMetadata.andReturn({cssclass: "icon-play", name: "Start"}); - mockPause.getMetadata.andReturn({cssclass: "icon-pause", name: "Pause"}); - mockStop.getMetadata.andReturn({cssclass: "icon-box", name: "Stop"}); + mockStart.getMetadata.andReturn({cssClass: "icon-play", name: "Start"}); + mockPause.getMetadata.andReturn({cssClass: "icon-pause", name: "Pause"}); + mockStop.getMetadata.andReturn({cssClass: "icon-box", name: "Stop"}); mockScope.domainObject = mockDomainObject; testModel = {}; @@ -152,7 +152,7 @@ define( expect(controller.text()).toEqual("0D 00:00:00"); }); - it("shows cssclass & name for the applicable start/pause action", function () { + 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");