From 285c8cbd1ef5521c5e598edea9ddfab6d8559483 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 18 Nov 2015 17:17:42 -0800 Subject: [PATCH 1/8] [Status] Add status tracking Add status tracking for domain objects, and decoration of representations with status-related classes. Supports WTD-1575 by allowing pending state of taxonomy to be handled by status tracking and custom CSS, instead of by overriding platform templates. --- platform/status/README.md | 2 + platform/status/bundle.json | 23 ++++++++ platform/status/src/StatusCapability.js | 56 +++++++++++++++++++ platform/status/src/StatusRepresenter.js | 71 ++++++++++++++++++++++++ platform/status/src/StatusService.js | 62 +++++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 platform/status/README.md create mode 100644 platform/status/bundle.json create mode 100644 platform/status/src/StatusCapability.js create mode 100644 platform/status/src/StatusRepresenter.js create mode 100644 platform/status/src/StatusService.js diff --git a/platform/status/README.md b/platform/status/README.md new file mode 100644 index 0000000000..4d8cb7e6fa --- /dev/null +++ b/platform/status/README.md @@ -0,0 +1,2 @@ +Facilitates tracking states associated with specific domain +objects. diff --git a/platform/status/bundle.json b/platform/status/bundle.json new file mode 100644 index 0000000000..4c117a9d75 --- /dev/null +++ b/platform/status/bundle.json @@ -0,0 +1,23 @@ +{ + "extensions": { + "representers": [ + { + "implementation": "StatusRepresenter.js" + } + ], + "capabilities": [ + { + "key": "status", + "implementation": "StatusCapability.js", + "depends": [ "statusService" ] + } + ], + "services": [ + { + "key": "statusService", + "implementation": "StatusService.js", + "depends": [ "topic" ] + } + ] + } +} diff --git a/platform/status/src/StatusCapability.js b/platform/status/src/StatusCapability.js new file mode 100644 index 0000000000..21e1dda037 --- /dev/null +++ b/platform/status/src/StatusCapability.js @@ -0,0 +1,56 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + function StatusCapability(statusService, domainObject) { + this.statusService = statusService; + this.domainObject = domainObject; + } + + StatusCapability.prototype.get = function () { + return this.statusService.getStatus(this.domainObject.getId()); + }; + + StatusCapability.prototype.set = function (status, state) { + return this.statusService.setStatus( + this.domainObject.getId(), + status, + state + ); + }; + + StatusCapability.prototype.listen = function (callback) { + return this.statusService.listen( + this.domainObject.getId(), + callback + ); + }; + + return StatusCapability; + + } +); diff --git a/platform/status/src/StatusRepresenter.js b/platform/status/src/StatusRepresenter.js new file mode 100644 index 0000000000..8f8187a3ee --- /dev/null +++ b/platform/status/src/StatusRepresenter.js @@ -0,0 +1,71 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + var STATUS_CLASS_PREFIX = "l-status-"; + + function StatusRepresenter(scope, element) { + this.element = element; + this.lastClasses = []; + } + + StatusRepresenter.prototype.represent = function (representation, domainObject) { + var self = this, + statusCapability = domainObject.getCapability('status'); + + function updateStatus(flags) { + var newClasses = flags.map(function (flag) { + return STATUS_CLASS_PREFIX + flag; + }); + + self.lastClasses.forEach(function (c) { + self.element.removeClass(c); + }); + + newClasses.forEach(function (c) { + self.element.addClass(c); + }); + + self.lastClasses = newClasses; + } + + updateStatus(statusCapability.get()); + this.unlisten = statusCapability.listen(updateStatus); + }; + + StatusRepresenter.prototype.destroy = function () { + if (this.unlisten) { + this.unlisten(); + this.unlisten = undefined; + } + }; + + + return StatusRepresenter; + + } +); diff --git a/platform/status/src/StatusService.js b/platform/status/src/StatusService.js new file mode 100644 index 0000000000..ed2c2bca6a --- /dev/null +++ b/platform/status/src/StatusService.js @@ -0,0 +1,62 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + var STATUS_PREFIX = "status:"; + + function StatusService(topic) { + this.statusTable = {}; + this.topic = topic; + } + + /** + * @returns {string[]} an array containing all status flags currently + * applicable to the object with this identifier + */ + StatusService.prototype.getStatus = function (id) { + return this.statusTable[id] || []; + }; + + StatusService.prototype.setStatus = function (id, status, state) { + this.statusTable[id] = this.statusTable[id] || []; + this.statusTable[id] = this.statusTable[id].filter(function (s) { + return s !== status; + }); + if (state) { + this.statusTable[id].push(status); + } + this.topic(STATUS_PREFIX + id).notify(this.statusTable[id]); + }; + + StatusService.prototype.listen = function (id, callback) { + return this.topic(STATUS_PREFIX + id).listen(callback); + }; + + return StatusService; + + } +); From 39d007470a4a62dac4a677e2498df00dba6f7b99 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 18 Nov 2015 17:23:31 -0800 Subject: [PATCH 2/8] [Status] Active platform/status bundle --- bundles.json | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles.json b/bundles.json index 6ebe71b189..f87f035720 100644 --- a/bundles.json +++ b/bundles.json @@ -29,6 +29,7 @@ "platform/policy", "platform/entanglement", "platform/search", + "platform/status", "example/imagery", "example/eventGenerator", From 29fdb6d641047c2cf294b761c59a6ecc725c4fd0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 Nov 2015 13:42:30 -0800 Subject: [PATCH 3/8] [Status] Add JSDoc --- platform/status/src/StatusCapability.js | 35 ++++++++++++++++++++++++ platform/status/src/StatusRepresenter.js | 17 +++++++++++- platform/status/src/StatusService.js | 30 ++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/platform/status/src/StatusCapability.js b/platform/status/src/StatusCapability.js index 21e1dda037..2751c198a9 100644 --- a/platform/status/src/StatusCapability.js +++ b/platform/status/src/StatusCapability.js @@ -26,15 +26,42 @@ define( function () { 'use strict'; + /** + * The `status` capability can be used to attach information + * about the state of a domain object, expressed as simple + * string flags. + * + * Representations of domain objects will also receive CSS + * classes which reflect their current status. + * (@see platform/status.StatusRepresenter) + * + * @param {platform/status.StatusService} statusService + * the service which will track domain object status + * within the application. + * @param {DomainObject} the domain object whose status will + * be tracked. + * @constructor + * @memberof platform/status + */ function StatusCapability(statusService, domainObject) { this.statusService = statusService; this.domainObject = domainObject; } + /** + * Get all status flags currently set for this domain object. + * @returns {string[]} all current status flags. + */ StatusCapability.prototype.get = function () { return this.statusService.getStatus(this.domainObject.getId()); }; + /** + * Set a status flag on this domain object. + * @param {string} status the status to set + * @param {boolean} state true if the domain object should + * possess this status, false if it should not + */ StatusCapability.prototype.set = function (status, state) { return this.statusService.setStatus( this.domainObject.getId(), @@ -43,6 +70,14 @@ define( ); }; + /** + * Listen for changes in this domain object's status. + * @param {Function} callback function to invoke on changes; + * called with the new status of the domain object, as an + * array of strings + * @returns {Function} a function which can be used to stop + * listening to status changes for this domain object. + */ StatusCapability.prototype.listen = function (callback) { return this.statusService.listen( this.domainObject.getId(), diff --git a/platform/status/src/StatusRepresenter.js b/platform/status/src/StatusRepresenter.js index 8f8187a3ee..4bee9d6651 100644 --- a/platform/status/src/StatusRepresenter.js +++ b/platform/status/src/StatusRepresenter.js @@ -26,8 +26,23 @@ define( function () { 'use strict'; - var STATUS_CLASS_PREFIX = "l-status-"; + var STATUS_CLASS_PREFIX = "s-status-"; + /** + * Adds/removes CSS classes to `mct-representation`s to reflect the + * current status of represented domain objects, as reported by + * their `status` capability. + * + * Statuses are prefixed with `s-status-` to build CSS class names. + * As such, when a domain object has the status "pending", its + * representations will have the CSS class `s-status-pending`. + * + * @param {angular.Scope} scope the representation's scope object + * @param element the representation's jqLite-wrapped DOM element + * @implements {Representer} + * @constructor + * @memberof platform/status + */ function StatusRepresenter(scope, element) { this.element = element; this.lastClasses = []; diff --git a/platform/status/src/StatusService.js b/platform/status/src/StatusService.js index ed2c2bca6a..b19cf7ca1d 100644 --- a/platform/status/src/StatusService.js +++ b/platform/status/src/StatusService.js @@ -28,12 +28,26 @@ define( var STATUS_PREFIX = "status:"; + /** + * The `statusService` maintains information about the current + * status of specific domain objects within the system. Status + * is represented as string flags which are present when a + * domain object possesses that status, and false when it does + * not. + * + * @param {platform/core.Topic} topic the `topic` service, used + * to create/use named listeners. + * @constructor + * @memberof platform/status + */ function StatusService(topic) { this.statusTable = {}; this.topic = topic; } /** + * Get all status flags currently set for a domain object. + * @param {string} id the identifier of the domain object * @returns {string[]} an array containing all status flags currently * applicable to the object with this identifier */ @@ -41,6 +55,13 @@ define( return this.statusTable[id] || []; }; + /** + * Set a status flag for a domain object. + * @param {string} id the identifier of the domain object + * @param {string} status the status to set + * @param {boolean} state true if the domain object should + * possess this status, false if it should not + */ StatusService.prototype.setStatus = function (id, status, state) { this.statusTable[id] = this.statusTable[id] || []; this.statusTable[id] = this.statusTable[id].filter(function (s) { @@ -52,6 +73,15 @@ define( this.topic(STATUS_PREFIX + id).notify(this.statusTable[id]); }; + /** + * Listen for changes in a domain object's status. + * @param {string} id the identifier of the domain object + * @param {Function} callback function to invoke on changes; + * called with the new status of the domain object, as an + * array of strings + * @returns {Function} a function which can be used to stop + * listening to status changes for this domain object. + */ StatusService.prototype.listen = function (id, callback) { return this.topic(STATUS_PREFIX + id).listen(callback); }; From 0d07c3c289e3a984b28059a483402d0e8ff73d11 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 Nov 2015 13:44:06 -0800 Subject: [PATCH 4/8] [Status] Clean up classes when destroyed --- platform/status/src/StatusRepresenter.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/platform/status/src/StatusRepresenter.js b/platform/status/src/StatusRepresenter.js index 4bee9d6651..0bab5fd571 100644 --- a/platform/status/src/StatusRepresenter.js +++ b/platform/status/src/StatusRepresenter.js @@ -48,6 +48,17 @@ define( this.lastClasses = []; } + /** + * Remove any status-related classes from this representation. + * @private + */ + StatusRepresenter.prototype.clearClasses = function () { + var element = this.element; + this.lastClasses.forEach(function (c) { + element.removeClass(c); + }); + }; + StatusRepresenter.prototype.represent = function (representation, domainObject) { var self = this, statusCapability = domainObject.getCapability('status'); @@ -57,9 +68,7 @@ define( return STATUS_CLASS_PREFIX + flag; }); - self.lastClasses.forEach(function (c) { - self.element.removeClass(c); - }); + self.clearClasses(); newClasses.forEach(function (c) { self.element.addClass(c); @@ -73,6 +82,7 @@ define( }; StatusRepresenter.prototype.destroy = function () { + this.clearClasses(); if (this.unlisten) { this.unlisten(); this.unlisten = undefined; From 3ffa6f70aa657fdbab31d13cbfac2810d7b4eac4 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 Nov 2015 13:47:39 -0800 Subject: [PATCH 5/8] [Status] Add empty specs --- platform/status/test/StatusCapabilitySpec.js | 32 +++++++++++++++++++ platform/status/test/StatusRepresenterSpec.js | 32 +++++++++++++++++++ platform/status/test/StatusServiceSpec.js | 32 +++++++++++++++++++ platform/status/test/suite.json | 5 +++ 4 files changed, 101 insertions(+) create mode 100644 platform/status/test/StatusCapabilitySpec.js create mode 100644 platform/status/test/StatusRepresenterSpec.js create mode 100644 platform/status/test/StatusServiceSpec.js create mode 100644 platform/status/test/suite.json diff --git a/platform/status/test/StatusCapabilitySpec.js b/platform/status/test/StatusCapabilitySpec.js new file mode 100644 index 0000000000..f016ebfa89 --- /dev/null +++ b/platform/status/test/StatusCapabilitySpec.js @@ -0,0 +1,32 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/StatusCapability"], + function (StatusCapability) { + "use strict"; + + describe("The status capability", function () { + }); + } +); diff --git a/platform/status/test/StatusRepresenterSpec.js b/platform/status/test/StatusRepresenterSpec.js new file mode 100644 index 0000000000..cd058401c1 --- /dev/null +++ b/platform/status/test/StatusRepresenterSpec.js @@ -0,0 +1,32 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/StatusRepresenter"], + function (StatusRepresenter) { + "use strict"; + + describe("The status representer", function () { + }); + } +); diff --git a/platform/status/test/StatusServiceSpec.js b/platform/status/test/StatusServiceSpec.js new file mode 100644 index 0000000000..f800002551 --- /dev/null +++ b/platform/status/test/StatusServiceSpec.js @@ -0,0 +1,32 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/StatusService"], + function (StatusService) { + "use strict"; + + describe("The status service", function () { + }); + } +); diff --git a/platform/status/test/suite.json b/platform/status/test/suite.json new file mode 100644 index 0000000000..8b0cf2fd26 --- /dev/null +++ b/platform/status/test/suite.json @@ -0,0 +1,5 @@ +[ + "StatusCapability", + "StatusRepresenter", + "StatusService" +] From 5b9e43f8ff354af232870228d794e5b3fafa2087 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 Nov 2015 14:22:44 -0800 Subject: [PATCH 6/8] [Status] Test platform/status --- platform/status/src/StatusConstants.js | 26 ++++++ platform/status/src/StatusRepresenter.js | 6 +- platform/status/src/StatusService.js | 6 +- platform/status/test/StatusCapabilitySpec.js | 52 +++++++++++ platform/status/test/StatusRepresenterSpec.js | 90 ++++++++++++++++++- platform/status/test/StatusServiceSpec.js | 62 +++++++++++++ 6 files changed, 234 insertions(+), 8 deletions(-) create mode 100644 platform/status/src/StatusConstants.js diff --git a/platform/status/src/StatusConstants.js b/platform/status/src/StatusConstants.js new file mode 100644 index 0000000000..75b1bca2e1 --- /dev/null +++ b/platform/status/src/StatusConstants.js @@ -0,0 +1,26 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define*/ +define({ + CSS_CLASS_PREFIX: 's-status-', + TOPIC_PREFIX: 'status:' +}); diff --git a/platform/status/src/StatusRepresenter.js b/platform/status/src/StatusRepresenter.js index 0bab5fd571..0808688c02 100644 --- a/platform/status/src/StatusRepresenter.js +++ b/platform/status/src/StatusRepresenter.js @@ -22,11 +22,11 @@ /*global define*/ define( - [], - function () { + ['./StatusConstants'], + function (StatusConstants) { 'use strict'; - var STATUS_CLASS_PREFIX = "s-status-"; + var STATUS_CLASS_PREFIX = StatusConstants.CSS_CLASS_PREFIX; /** * Adds/removes CSS classes to `mct-representation`s to reflect the diff --git a/platform/status/src/StatusService.js b/platform/status/src/StatusService.js index b19cf7ca1d..6fff6f49d8 100644 --- a/platform/status/src/StatusService.js +++ b/platform/status/src/StatusService.js @@ -22,11 +22,11 @@ /*global define*/ define( - [], - function () { + ['./StatusConstants'], + function (StatusConstants) { 'use strict'; - var STATUS_PREFIX = "status:"; + var STATUS_PREFIX = StatusConstants.TOPIC_PREFIX; /** * The `statusService` maintains information about the current diff --git a/platform/status/test/StatusCapabilitySpec.js b/platform/status/test/StatusCapabilitySpec.js index f016ebfa89..481abf6a53 100644 --- a/platform/status/test/StatusCapabilitySpec.js +++ b/platform/status/test/StatusCapabilitySpec.js @@ -27,6 +27,58 @@ define( "use strict"; describe("The status capability", function () { + var mockStatusService, + mockDomainObject, + mockUnlisten, + testId, + testStatusFlags, + capability; + + beforeEach(function () { + testId = "some-id"; + testStatusFlags = [ 'a', 'b', 'c' ]; + + mockStatusService = jasmine.createSpyObj( + 'statusService', + [ 'listen', 'setStatus', 'getStatus' ] + ); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getId', 'getCapability', 'getModel' ] + ); + mockUnlisten = jasmine.createSpy('unlisten'); + + mockStatusService.listen.andReturn(mockUnlisten); + mockStatusService.getStatus.andReturn(testStatusFlags); + mockDomainObject.getId.andReturn(testId); + + capability = new StatusCapability( + mockStatusService, + mockDomainObject + ); + }); + + it("sets status with the statusService", function () { + var testStatus = "some-test-status"; + capability.set(testStatus, true); + expect(mockStatusService.setStatus) + .toHaveBeenCalledWith(testId, testStatus, true); + capability.set(testStatus, false); + expect(mockStatusService.setStatus) + .toHaveBeenCalledWith(testId, testStatus, false); + }); + + it("gets status from the statusService", function () { + expect(capability.get()).toBe(testStatusFlags); + }); + + it("listens to changes from the statusService", function () { + var mockCallback = jasmine.createSpy(); + expect(capability.listen(mockCallback)) + .toBe(mockUnlisten); + expect(mockStatusService.listen) + .toHaveBeenCalledWith(testId, mockCallback); + }); }); } ); diff --git a/platform/status/test/StatusRepresenterSpec.js b/platform/status/test/StatusRepresenterSpec.js index cd058401c1..e9191587a7 100644 --- a/platform/status/test/StatusRepresenterSpec.js +++ b/platform/status/test/StatusRepresenterSpec.js @@ -22,11 +22,97 @@ /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ define( - ["../src/StatusRepresenter"], - function (StatusRepresenter) { + ["../src/StatusRepresenter", "../src/StatusConstants"], + function (StatusRepresenter, StatusConstants) { "use strict"; describe("The status representer", function () { + var mockScope, + mockElement, + testRepresentation, + mockDomainObject, + mockStatusCapability, + mockUnlisten, + elementClasses, + testStatusFlags, + representer; + + function verifyClasses() { + expect(Object.keys(elementClasses).sort()) + .toEqual(testStatusFlags.map(function (s) { + return StatusConstants.CSS_CLASS_PREFIX + s; + }).sort()); + } + + function updateStatus(newFlags) { + testStatusFlags = newFlags; + mockStatusCapability.get.andReturn(newFlags); + mockStatusCapability.listen.mostRecentCall + .args[0](newFlags); + } + + beforeEach(function () { + testStatusFlags = [ 'x', 'y', 'z' ]; + + mockScope = {}; + mockElement = jasmine.createSpyObj('element', [ + 'addClass', + 'removeClass' + ]); + testRepresentation = { key: "someKey" }; + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getModel', 'getId', 'getCapability' ] + ); + mockStatusCapability = jasmine.createSpyObj( + 'status', + [ 'get', 'set', 'listen' ] + ); + mockUnlisten = jasmine.createSpy(); + + elementClasses = {}; + + mockElement.addClass.andCallFake(function (c) { + elementClasses[c] = true; + }); + mockElement.removeClass.andCallFake(function (c) { + delete elementClasses[c]; + }); + + mockStatusCapability.get.andReturn(testStatusFlags); + mockStatusCapability.listen.andReturn(mockUnlisten); + + mockDomainObject.getCapability.andCallFake(function (c) { + return c === 'status' && mockStatusCapability; + }); + + representer = new StatusRepresenter(mockScope, mockElement); + representer.represent(testRepresentation, mockDomainObject); + }); + + it("listens for status changes", function () { + expect(mockStatusCapability.listen) + .toHaveBeenCalledWith(jasmine.any(Function)); + }); + + it("initially sets classes to reflect status", verifyClasses); + + it("changes classes on status change callbacks", function () { + updateStatus(['a', 'x', '123']); + verifyClasses(); + }); + + it("stops listening when destroyed", function () { + expect(mockUnlisten).not.toHaveBeenCalled(); + representer.destroy(); + expect(mockUnlisten).toHaveBeenCalled(); + }); + + it("removes status classes when destroyed", function () { + expect(elementClasses).not.toEqual({}); + representer.destroy(); + expect(elementClasses).toEqual({}); + }); }); } ); diff --git a/platform/status/test/StatusServiceSpec.js b/platform/status/test/StatusServiceSpec.js index f800002551..1f85cd70a1 100644 --- a/platform/status/test/StatusServiceSpec.js +++ b/platform/status/test/StatusServiceSpec.js @@ -27,6 +27,68 @@ define( "use strict"; describe("The status service", function () { + var mockTopic, + mockTopicInstance, + mockUnlisten, + mockCallback, + testId, + testStatus, + statusService; + + beforeEach(function () { + testId = "some-domain-object-identifier"; + testStatus = "test-status"; + + mockTopic = jasmine.createSpy('topic'); + mockTopicInstance = jasmine.createSpyObj( + 'topicInstance', + [ 'notify', 'listen' ] + ); + mockUnlisten = jasmine.createSpy('unlisten'); + mockCallback = jasmine.createSpy('callback'); + + mockTopic.andReturn(mockTopicInstance); + mockTopicInstance.listen.andReturn(mockUnlisten); + + statusService = new StatusService(mockTopic); + }); + + it("initially contains no flags for an object", function () { + expect(statusService.getStatus(testId)).toEqual([]); + }); + + it("stores and clears status flags", function () { + statusService.setStatus(testId, testStatus, true); + expect(statusService.getStatus(testId)).toEqual([testStatus]); + statusService.setStatus(testId, testStatus, false); + expect(statusService.getStatus(testId)).toEqual([]); + }); + + it("uses topic to listen for changes", function () { + expect(statusService.listen(testId, mockCallback)) + .toEqual(mockUnlisten); + expect(mockTopic) + .toHaveBeenCalledWith(jasmine.any(String)); + // Just care that the topic was somehow unique to the object + expect(mockTopic.mostRecentCall.args[0].indexOf(testId)) + .not.toEqual(-1); + }); + + it("notifies listeners of changes", function () { + statusService.setStatus(testId, testStatus, true); + expect(mockTopicInstance.notify) + .toHaveBeenCalledWith([ testStatus ]); + statusService.setStatus(testId, testStatus, false); + expect(mockTopicInstance.notify) + .toHaveBeenCalledWith([ ]); + + expect(mockTopic) + .toHaveBeenCalledWith(jasmine.any(String)); + // Just care that the topic was somehow unique to the object + expect(mockTopic.mostRecentCall.args[0].indexOf(testId)) + .not.toEqual(-1); + }); + }); } ); From b5d1118a3ff8f7d1f36db846bf9270cd21571ba7 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 Nov 2015 15:12:03 -0800 Subject: [PATCH 7/8] [Status] Document status capability ...in the developer guide. Includes a table for listing status names and classes, per code review feedback, nasa/openmctweb#319. --- docs/src/guide/index.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index a0159ec672..6271337792 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -2056,6 +2056,31 @@ objects which has a `relationships` property in their model, whose value is an object containing key-value pairs, where keys are strings identifying relationship types, and values are arrays of domain object identifiers. +## Status Capability + +The `status` capability provides a way to flag domain objects as possessing +certain states, represented as simple strings. These states, in turn, are +reflected on `mct-representation` elements as classes (prefixed with +`s-status-`.) The `status` capability has the following interface: + +* `get()`: Returns an array of all status strings that currently apply + to this object. +* `set(status, state)`: Adds or removes a status flag to this domain object. + The `status` argument is the string to set; `state` is a boolean + indicating whether this status should be included (true) or removed (false). +* `listen(callback)`: Listen for changes in status. The provided `callback` + will be invoked with an array of all current status strings whenever status + changes. + +Plug-ins may add and/or recognize arbitrary status flags. Flags defined +and/or supported by the platform are: + + Status | CSS Class | Meaning +-----------|--------------------|----------------------------------- +`editing` | `s-status-editing` | Domain object is being edited. +`pending` | `s-status-pending` | Domain object is partially loaded. + + ## Telemetry Capability The telemetry capability provides a means for accessing telemetry data From 400b992ec36b4572175b160549ea31d6771f96ac Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 20 Nov 2015 09:46:08 -0800 Subject: [PATCH 8/8] [Status] Revise API Change method names, add a getter to status capability; per code review feedback, nasa/openmctweb#319. --- platform/status/src/StatusCapability.js | 15 ++++++++++++--- platform/status/src/StatusRepresenter.js | 2 +- platform/status/src/StatusService.js | 2 +- platform/status/test/StatusCapabilitySpec.js | 11 ++++++++--- platform/status/test/StatusRepresenterSpec.js | 4 ++-- platform/status/test/StatusServiceSpec.js | 6 +++--- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/platform/status/src/StatusCapability.js b/platform/status/src/StatusCapability.js index 2751c198a9..6b0221d188 100644 --- a/platform/status/src/StatusCapability.js +++ b/platform/status/src/StatusCapability.js @@ -49,11 +49,20 @@ define( } /** - * Get all status flags currently set for this domain object. + * List all status flags currently set for this domain object. * @returns {string[]} all current status flags. */ - StatusCapability.prototype.get = function () { - return this.statusService.getStatus(this.domainObject.getId()); + StatusCapability.prototype.list = function () { + return this.statusService.listStatuses(this.domainObject.getId()); + }; + + /** + * Check if a status flag is currently set for this domain object. + * @param {string} status the status to get + * @returns {boolean} true if the flag is present, otherwise false + */ + StatusCapability.prototype.get = function (status) { + return this.list().indexOf(status) !== -1; }; /** diff --git a/platform/status/src/StatusRepresenter.js b/platform/status/src/StatusRepresenter.js index 0808688c02..550fec3e6d 100644 --- a/platform/status/src/StatusRepresenter.js +++ b/platform/status/src/StatusRepresenter.js @@ -77,7 +77,7 @@ define( self.lastClasses = newClasses; } - updateStatus(statusCapability.get()); + updateStatus(statusCapability.list()); this.unlisten = statusCapability.listen(updateStatus); }; diff --git a/platform/status/src/StatusService.js b/platform/status/src/StatusService.js index 6fff6f49d8..d75e935429 100644 --- a/platform/status/src/StatusService.js +++ b/platform/status/src/StatusService.js @@ -51,7 +51,7 @@ define( * @returns {string[]} an array containing all status flags currently * applicable to the object with this identifier */ - StatusService.prototype.getStatus = function (id) { + StatusService.prototype.listStatuses = function (id) { return this.statusTable[id] || []; }; diff --git a/platform/status/test/StatusCapabilitySpec.js b/platform/status/test/StatusCapabilitySpec.js index 481abf6a53..1bd3326c4e 100644 --- a/platform/status/test/StatusCapabilitySpec.js +++ b/platform/status/test/StatusCapabilitySpec.js @@ -40,7 +40,7 @@ define( mockStatusService = jasmine.createSpyObj( 'statusService', - [ 'listen', 'setStatus', 'getStatus' ] + [ 'listen', 'setStatus', 'listStatuses' ] ); mockDomainObject = jasmine.createSpyObj( 'domainObject', @@ -49,7 +49,7 @@ define( mockUnlisten = jasmine.createSpy('unlisten'); mockStatusService.listen.andReturn(mockUnlisten); - mockStatusService.getStatus.andReturn(testStatusFlags); + mockStatusService.listStatuses.andReturn(testStatusFlags); mockDomainObject.getId.andReturn(testId); capability = new StatusCapability( @@ -69,7 +69,7 @@ define( }); it("gets status from the statusService", function () { - expect(capability.get()).toBe(testStatusFlags); + expect(capability.list()).toBe(testStatusFlags); }); it("listens to changes from the statusService", function () { @@ -79,6 +79,11 @@ define( expect(mockStatusService.listen) .toHaveBeenCalledWith(testId, mockCallback); }); + + it("allows statuses to be checked individually", function () { + expect(capability.get('some-unset-status')).toBe(false); + expect(capability.get(testStatusFlags[0])).toBe(true); + }); }); } ); diff --git a/platform/status/test/StatusRepresenterSpec.js b/platform/status/test/StatusRepresenterSpec.js index e9191587a7..1d305ea983 100644 --- a/platform/status/test/StatusRepresenterSpec.js +++ b/platform/status/test/StatusRepresenterSpec.js @@ -66,7 +66,7 @@ define( ); mockStatusCapability = jasmine.createSpyObj( 'status', - [ 'get', 'set', 'listen' ] + [ 'list', 'get', 'set', 'listen' ] ); mockUnlisten = jasmine.createSpy(); @@ -79,7 +79,7 @@ define( delete elementClasses[c]; }); - mockStatusCapability.get.andReturn(testStatusFlags); + mockStatusCapability.list.andReturn(testStatusFlags); mockStatusCapability.listen.andReturn(mockUnlisten); mockDomainObject.getCapability.andCallFake(function (c) { diff --git a/platform/status/test/StatusServiceSpec.js b/platform/status/test/StatusServiceSpec.js index 1f85cd70a1..c064af6bc8 100644 --- a/platform/status/test/StatusServiceSpec.js +++ b/platform/status/test/StatusServiceSpec.js @@ -54,14 +54,14 @@ define( }); it("initially contains no flags for an object", function () { - expect(statusService.getStatus(testId)).toEqual([]); + expect(statusService.listStatuses(testId)).toEqual([]); }); it("stores and clears status flags", function () { statusService.setStatus(testId, testStatus, true); - expect(statusService.getStatus(testId)).toEqual([testStatus]); + expect(statusService.listStatuses(testId)).toEqual([testStatus]); statusService.setStatus(testId, testStatus, false); - expect(statusService.getStatus(testId)).toEqual([]); + expect(statusService.listStatuses(testId)).toEqual([]); }); it("uses topic to listen for changes", function () {