diff --git a/bundles.json b/bundles.json index 79ab2dd246..35d6f11728 100644 --- a/bundles.json +++ b/bundles.json @@ -19,13 +19,13 @@ "platform/features/events", "platform/forms", "platform/identity", + "platform/persistence/local", "platform/persistence/queue", "platform/policy", "platform/entanglement", "platform/search", "example/imagery", - "example/persistence", "example/eventGenerator", "example/generator" ] diff --git a/example/localstorage/src/LocalStoragePersistenceProvider.js b/example/localstorage/src/LocalStoragePersistenceProvider.js deleted file mode 100644 index 8a367ca333..0000000000 --- a/example/localstorage/src/LocalStoragePersistenceProvider.js +++ /dev/null @@ -1,86 +0,0 @@ -/***************************************************************************** - * 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,localStorage*/ -/** - * Stubbed implementation of a persistence provider, - * to permit objects to be created, saved, etc. - */ -define( - [], - function () { - 'use strict'; - - function BrowserPersistenceProvider($q, SPACE) { - var spaces = SPACE ? [SPACE] : [], - promises = { - as: function (value) { - return $q.when(value); - } - }, - provider; - - function setValue(key, value) { - localStorage[key] = JSON.stringify(value); - } - - function getValue(key) { - if (localStorage[key]) { - return JSON.parse(localStorage[key]); - } - return {}; - } - - provider = { - listSpaces: function () { - return promises.as(spaces); - }, - listObjects: function (space) { - var space_obj = getValue(space); - return promises.as(Object.keys(space_obj)); - }, - createObject: function (space, key, value) { - var space_obj = getValue(space); - space_obj[key] = value; - setValue(space, space_obj); - return promises.as(true); - }, - readObject: function (space, key) { - var space_obj = getValue(space); - return promises.as(space_obj[key]); - }, - deleteObject: function (space, key, value) { - var space_obj = getValue(space); - delete space_obj[key]; - return promises.as(true); - } - }; - - provider.updateObject = provider.createObject; - - return provider; - - } - - return BrowserPersistenceProvider; - } -); diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 2ab488ba12..602538c47a 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -139,7 +139,7 @@ { "key": "mctSplitPane", "implementation": "directives/MCTSplitPane.js", - "depends": [ "$parse", "$log" ] + "depends": [ "$parse", "$log", "$interval" ] }, { "key": "mctSplitter", diff --git a/platform/commonUI/general/src/directives/MCTSplitPane.js b/platform/commonUI/general/src/directives/MCTSplitPane.js index 688689d79f..abc54f772e 100644 --- a/platform/commonUI/general/src/directives/MCTSplitPane.js +++ b/platform/commonUI/general/src/directives/MCTSplitPane.js @@ -28,6 +28,7 @@ define( // Pixel width to allocate for the splitter itself var DEFAULT_ANCHOR = 'left', + POLLING_INTERVAL = 15, // milliseconds CHILDREN_WARNING_MESSAGE = [ "Invalid mct-split-pane contents.", "This element should contain exactly three", @@ -94,7 +95,7 @@ define( * @memberof platform/commonUI/general * @constructor */ - function MCTSplitPane($parse, $log) { + function MCTSplitPane($parse, $log, $interval) { var anchors = { left: true, right: true, @@ -105,6 +106,7 @@ define( function controller($scope, $element, $attrs) { var anchorKey = $attrs.anchor || DEFAULT_ANCHOR, anchor, + activeInterval, positionParsed = $parse($attrs.position), position; // Start undefined, until explicitly set @@ -162,14 +164,14 @@ define( // Getter-setter for the pixel offset of the splitter, // relative to the current edge. function getSetPosition(value) { - var min, max; + var min, max, prior = position; if (typeof value === 'number') { position = value; enforceExtrema(); updateElementPositions(); // Pass change up so this state can be shared - if (positionParsed.assign) { + if (positionParsed.assign && position !== prior) { positionParsed.assign($scope, position); } } @@ -193,6 +195,16 @@ define( $element.children().eq(anchor.reversed ? 2 : 0)[0] )); + // And poll for position changes enforced by styles + activeInterval = $interval(function () { + getSetPosition(getSetPosition()); + }, POLLING_INTERVAL, false); + + // ...and stop polling when we're destroyed. + $scope.$on('$destroy', function () { + $interval.cancel(activeInterval); + }); + // Interface exposed by controller, for mct-splitter to user return { position: getSetPosition, diff --git a/platform/commonUI/general/src/directives/MCTSplitter.js b/platform/commonUI/general/src/directives/MCTSplitter.js index 5216c69358..c163c107e0 100644 --- a/platform/commonUI/general/src/directives/MCTSplitter.js +++ b/platform/commonUI/general/src/directives/MCTSplitter.js @@ -48,10 +48,6 @@ define( element.addClass("splitter"); - // Now that we have the above class, the splitter width - // will have changed, so trigger a positioning update. - mctSplitPane.position(mctSplitPane.position()); - scope.splitter = { // Begin moving this splitter startMove: function () { diff --git a/platform/core/src/capabilities/CoreCapabilityProvider.js b/platform/core/src/capabilities/CoreCapabilityProvider.js index 7b1ba070d7..8240f9fa89 100644 --- a/platform/core/src/capabilities/CoreCapabilityProvider.js +++ b/platform/core/src/capabilities/CoreCapabilityProvider.js @@ -67,8 +67,9 @@ define( function packageCapabilities(capabilities) { var result = {}; capabilities.forEach(function (capability) { - if (capability.key && !result[capability.key]) { - result[capability.key] = capability; + if (capability.key) { + result[capability.key] = + result[capability.key] || capability; } else { $log.warn("No key defined for capability; skipping."); } diff --git a/platform/core/src/capabilities/MetadataCapability.js b/platform/core/src/capabilities/MetadataCapability.js index ea48d79042..ddb5204edb 100644 --- a/platform/core/src/capabilities/MetadataCapability.js +++ b/platform/core/src/capabilities/MetadataCapability.js @@ -78,10 +78,6 @@ define( { name: "Type", value: type && type.getName() - }, - { - name: "ID", - value: domainObject.getId() } ]; } diff --git a/platform/core/test/capabilities/CoreCapabilityProviderSpec.js b/platform/core/test/capabilities/CoreCapabilityProviderSpec.js index ae42d02ccb..26df72c4d7 100644 --- a/platform/core/test/capabilities/CoreCapabilityProviderSpec.js +++ b/platform/core/test/capabilities/CoreCapabilityProviderSpec.js @@ -86,7 +86,19 @@ define( expect(mockLog.warn).not.toHaveBeenCalled(); }); + it("prefers higher-priority capability", function () { + KeylessCapability.key = BasicCapability.key; + expect(provider.getCapabilities({}).basic) + .toEqual(BasicCapability); + }); + + // https://github.com/nasa/openmctweb/issues/49 + it("does not log a warning for multiple capabilities with the same key", function () { + KeylessCapability.key = BasicCapability.key; + provider.getCapabilities({}); + expect(mockLog.warn).not.toHaveBeenCalled(); + }); }); } -); \ No newline at end of file +); diff --git a/platform/core/test/capabilities/MetadataCapabilitySpec.js b/platform/core/test/capabilities/MetadataCapabilitySpec.js index 272746f037..0a04e1d1ea 100644 --- a/platform/core/test/capabilities/MetadataCapabilitySpec.js +++ b/platform/core/test/capabilities/MetadataCapabilitySpec.js @@ -92,7 +92,6 @@ define( it("reports generic properties", function () { var properties = metadata.invoke(); - expect(findValue(properties, 'ID')).toEqual("Test id"); expect(findValue(properties, 'Type')).toEqual("Test type"); }); diff --git a/example/localstorage/bundle.json b/platform/persistence/local/bundle.json similarity index 77% rename from example/localstorage/bundle.json rename to platform/persistence/local/bundle.json index b164f9aaa9..e5d338d21d 100644 --- a/example/localstorage/bundle.json +++ b/platform/persistence/local/bundle.json @@ -13,6 +13,11 @@ "key": "PERSISTENCE_SPACE", "value": "mct" } + ], + "indicators": [ + { + "implementation": "LocalStorageIndicator.js" + } ] } -} \ No newline at end of file +} diff --git a/platform/persistence/local/src/LocalStorageIndicator.js b/platform/persistence/local/src/LocalStorageIndicator.js new file mode 100644 index 0000000000..df6f868ead --- /dev/null +++ b/platform/persistence/local/src/LocalStorageIndicator.js @@ -0,0 +1,60 @@ +/***************************************************************************** + * 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,window*/ + +define( + [], + function () { + "use strict"; + + var LOCAL_STORAGE_WARNING = [ + "Using browser local storage for persistence.", + "Anything you create or change will be visible only", + "in this browser on this machine." + ].join(' '); + + /** + * Indicator for local storage persistence. Provides a minimum + * level of feedback indicating that local storage is in use. + * @constructor + * @memberof platform/persistence/local + * @implements {Indicator} + */ + function LocalStorageIndicator() { + } + + LocalStorageIndicator.prototype.getGlyph = function () { + return "D"; + }; + LocalStorageIndicator.prototype.getGlyphClass = function () { + return 'caution'; + }; + LocalStorageIndicator.prototype.getText = function () { + return "Off-line storage"; + }; + LocalStorageIndicator.prototype.getDescription = function () { + return LOCAL_STORAGE_WARNING; + }; + + return LocalStorageIndicator; + } +); diff --git a/platform/persistence/local/src/LocalStoragePersistenceProvider.js b/platform/persistence/local/src/LocalStoragePersistenceProvider.js new file mode 100644 index 0000000000..0f87b46a45 --- /dev/null +++ b/platform/persistence/local/src/LocalStoragePersistenceProvider.js @@ -0,0 +1,97 @@ +/***************************************************************************** + * 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,window*/ + +define( + [], + function () { + 'use strict'; + + /** + * The LocalStoragePersistenceProvider reads and writes JSON documents + * (more specifically, domain object models) to/from the browser's + * local storage. + * @memberof platform/persistence/local + * @constructor + * @implements {PersistenceService} + * @param q Angular's $q, for promises + * @param $interval Angular's $interval service + * @param {string} space the name of the persistence space being served + */ + function LocalStoragePersistenceProvider($q, space) { + this.$q = $q; + this.space = space; + this.spaces = space ? [space] : []; + this.localStorage = window.localStorage; + } + + /** + * Set a value in local storage. + * @private + */ + LocalStoragePersistenceProvider.prototype.setValue = function (key, value) { + this.localStorage[key] = JSON.stringify(value); + }; + + /** + * Get a value from local storage. + * @private + */ + LocalStoragePersistenceProvider.prototype.getValue = function (key) { + return this.localStorage[key] ? + JSON.parse(this.localStorage[key]) : {}; + }; + + LocalStoragePersistenceProvider.prototype.listSpaces = function () { + return this.$q.when(this.spaces); + }; + + LocalStoragePersistenceProvider.prototype.listObjects = function (space) { + return this.$q.when(Object.keys(this.getValue(space))); + }; + + LocalStoragePersistenceProvider.prototype.createObject = function (space, key, value) { + var spaceObj = this.getValue(space); + spaceObj[key] = value; + this.setValue(space, spaceObj); + return this.$q.when(true); + }; + + LocalStoragePersistenceProvider.prototype.readObject = function (space, key) { + var spaceObj = this.getValue(space); + return this.$q.when(spaceObj[key]); + }; + + LocalStoragePersistenceProvider.prototype.deleteObject = function (space, key, value) { + var spaceObj = this.getValue(space); + delete spaceObj[key]; + this.setValue(space, spaceObj); + return this.$q.when(true); + }; + + LocalStoragePersistenceProvider.prototype.updateObject = + LocalStoragePersistenceProvider.prototype.createObject; + + return LocalStoragePersistenceProvider; + } +); diff --git a/platform/persistence/local/test/LocalStorageIndicatorSpec.js b/platform/persistence/local/test/LocalStorageIndicatorSpec.js new file mode 100644 index 0000000000..0e71ebceba --- /dev/null +++ b/platform/persistence/local/test/LocalStorageIndicatorSpec.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,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/LocalStorageIndicator"], + function (LocalStorageIndicator) { + "use strict"; + + describe("The local storage status indicator", function () { + var indicator; + + beforeEach(function () { + indicator = new LocalStorageIndicator(); + }); + + it("provides text to display in status area", function () { + // Don't particularly care what is there so long + // as interface is appropriately implemented. + expect(indicator.getText()).toEqual(jasmine.any(String)); + }); + + it("has a database icon", function () { + expect(indicator.getGlyph()).toEqual("D"); + }); + + it("has a 'caution' class to draw attention", function () { + expect(indicator.getGlyphClass()).toEqual("caution"); + }); + + it("provides a description for a tooltip", function () { + // Just want some non-empty string here. Providing a + // message here is important but don't want to test wording. + var description = indicator.getDescription(); + expect(description).toEqual(jasmine.any(String)); + expect(description.length).not.toEqual(0); + }); + + + + }); + } +); diff --git a/platform/persistence/local/test/LocalStoragePersistenceProviderSpec.js b/platform/persistence/local/test/LocalStoragePersistenceProviderSpec.js new file mode 100644 index 0000000000..5d42117164 --- /dev/null +++ b/platform/persistence/local/test/LocalStoragePersistenceProviderSpec.js @@ -0,0 +1,120 @@ +/***************************************************************************** + * 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,afterEach,waitsFor,jasmine*/ + + +define( + ["../src/LocalStoragePersistenceProvider"], + function (LocalStoragePersistenceProvider) { + "use strict"; + + describe("The local storage persistence provider", function () { + var mockQ, + testSpace = "testSpace", + mockCallback, + testLocalStorage, + provider; + + function mockPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return mockPromise(callback(value)); + } + }; + } + + beforeEach(function () { + testLocalStorage = {}; + + mockQ = jasmine.createSpyObj("$q", ["when", "reject"]); + mockCallback = jasmine.createSpy('callback'); + + mockQ.when.andCallFake(mockPromise); + + provider = new LocalStoragePersistenceProvider( + mockQ, + testSpace, + testLocalStorage + ); + + // White-boxy: Can't effectively mock window.localStorage, + // so override the provider's local reference to it. + provider.localStorage = testLocalStorage; + }); + + it("reports available spaces", function () { + provider.listSpaces().then(mockCallback); + expect(mockCallback).toHaveBeenCalledWith([testSpace]); + }); + + it("lists all available documents", function () { + provider.listObjects(testSpace).then(mockCallback); + expect(mockCallback.mostRecentCall.args[0]).toEqual([]); + provider.createObject(testSpace, 'abc', { a: 42 }); + provider.listObjects(testSpace).then(mockCallback); + expect(mockCallback.mostRecentCall.args[0]).toEqual(['abc']); + }); + + it("allows object creation", function () { + var model = { someKey: "some value" }; + provider.createObject(testSpace, "abc", model) + .then(mockCallback); + expect(JSON.parse(testLocalStorage[testSpace]).abc) + .toEqual(model); + expect(mockCallback.mostRecentCall.args[0]).toBeTruthy(); + }); + + it("allows object models to be read back", function () { + var model = { someKey: "some other value" }; + testLocalStorage[testSpace] = JSON.stringify({ abc: model }); + provider.readObject(testSpace, "abc").then(mockCallback); + expect(mockCallback).toHaveBeenCalledWith(model); + }); + + it("allows object update", function () { + var model = { someKey: "some new value" }; + testLocalStorage[testSpace] = JSON.stringify({ + abc: { somethingElse: 42 } + }); + provider.updateObject(testSpace, "abc", model) + .then(mockCallback); + expect(JSON.parse(testLocalStorage[testSpace]).abc) + .toEqual(model); + }); + + it("allows object deletion", function () { + testLocalStorage[testSpace] = JSON.stringify({ + abc: { somethingElse: 42 } + }); + provider.deleteObject(testSpace, "abc").then(mockCallback); + expect(testLocalStorage[testSpace].abc) + .toBeUndefined(); + }); + + it("returns undefined when objects are not found", function () { + provider.readObject("testSpace", "abc").then(mockCallback); + expect(mockCallback).toHaveBeenCalledWith(undefined); + }); + + }); + } +); diff --git a/platform/persistence/local/test/suite.json b/platform/persistence/local/test/suite.json new file mode 100644 index 0000000000..057958d7a4 --- /dev/null +++ b/platform/persistence/local/test/suite.json @@ -0,0 +1,4 @@ +[ + "LocalStorageIndicator", + "LocalStoragePersistenceProvider" +]