diff --git a/bundles.json b/bundles.json index c0dbd4b974..6b0608cdff 100644 --- a/bundles.json +++ b/bundles.json @@ -2,6 +2,7 @@ "platform/framework", "platform/core", "platform/representation", + "platform/commonUI/about", "platform/commonUI/browse", "platform/commonUI/edit", "platform/commonUI/dialog", diff --git a/platform/commonUI/about/README.md b/platform/commonUI/about/README.md new file mode 100644 index 0000000000..05bcfe22cd --- /dev/null +++ b/platform/commonUI/about/README.md @@ -0,0 +1,26 @@ +The "about" bundle provides the default lower-right application logo, +as well as the dialog it launches when clicked. + +# Extensions + +The About dialog contains several line items to display different +version properties (e.g. when built, et cetera.) Plug-ins may wish +to introduce additional line items here, in particular if the +platform is used to build a separately-branded piece of software. + +This bundle introduces the `versions` extension category to support this. +An extension of this category is implementation-less (all information +is contained within its declaration) and should include the following +fields: + +* `name`: The name to display for this version line-item; this may + be the name of the software, or something else such as "Built". +* `value`: The value to display corresponding to this line-item; + this is typically a version number, revision identifier, or + human-readable date. +* `description`: Optional; a longer-form description of this line + item, to display in a tooltip. + +Ordering of these line items is handled by extension priority; see framework +documentation (`platform/framework/README.md`) for information on how +this ordering is handled. diff --git a/platform/commonUI/about/bundle.json b/platform/commonUI/about/bundle.json new file mode 100644 index 0000000000..142241449c --- /dev/null +++ b/platform/commonUI/about/bundle.json @@ -0,0 +1,43 @@ +{ + "name": "About Open MCT Web", + "extensions": { + "templates": [ + { + "key": "app-logo", + "priority": "optional", + "templateUrl": "templates/app-logo.html" + }, + { + "key": "about-logo", + "priority": "preferred", + "templateUrl": "templates/about-logo.html" + }, + { + "key": "about-dialog", + "templateUrl": "templates/about-dialog.html" + }, + { + "key": "overlay-about", + "templateUrl": "templates/overlay-about.html" + } + ], + "controllers": [ + { + "key": "LogoController", + "depends": [ "overlayService" ], + "implementation": "LogoController.js" + }, + { + "key": "AboutController", + "depends": [ "versions[]", "$window" ], + "implementation": "AboutController.js" + } + ], + "routes": [ + { + "when": "/licenses", + "templateUrl": "templates/licenses.html" + } + ] + } +} \ No newline at end of file diff --git a/platform/commonUI/about/res/templates/about-dialog.html b/platform/commonUI/about/res/templates/about-dialog.html new file mode 100644 index 0000000000..7840b8860b --- /dev/null +++ b/platform/commonUI/about/res/templates/about-dialog.html @@ -0,0 +1,12 @@ +
+ This is a placeholder for the about dialog. + + Show licenses. + +

+ + {{version.name}} + {{version.value}} + +

+
diff --git a/platform/commonUI/about/res/templates/about-logo.html b/platform/commonUI/about/res/templates/about-logo.html new file mode 100644 index 0000000000..2b658c2696 --- /dev/null +++ b/platform/commonUI/about/res/templates/about-logo.html @@ -0,0 +1,4 @@ + + + + diff --git a/platform/commonUI/about/res/templates/app-logo.html b/platform/commonUI/about/res/templates/app-logo.html new file mode 100644 index 0000000000..8b3cefb5c0 --- /dev/null +++ b/platform/commonUI/about/res/templates/app-logo.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/platform/commonUI/about/res/templates/licenses.html b/platform/commonUI/about/res/templates/licenses.html new file mode 100644 index 0000000000..c3af087cd9 --- /dev/null +++ b/platform/commonUI/about/res/templates/licenses.html @@ -0,0 +1 @@ +Placeholder for open source licenses. \ No newline at end of file diff --git a/platform/commonUI/about/res/templates/overlay-about.html b/platform/commonUI/about/res/templates/overlay-about.html new file mode 100644 index 0000000000..f9af8cd923 --- /dev/null +++ b/platform/commonUI/about/res/templates/overlay-about.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/platform/commonUI/about/src/AboutController.js b/platform/commonUI/about/src/AboutController.js new file mode 100644 index 0000000000..77c6ede2cb --- /dev/null +++ b/platform/commonUI/about/src/AboutController.js @@ -0,0 +1,42 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * The AboutController provides information to populate the + * About dialog. + * @constructor + * @param {object[]} versions an array of version extensions; + * injected from `versions[]` + * @param $window Angular-injected window object + */ + function AboutController(versions, $window) { + return { + /** + * Get version info. This is given as an array of + * objects, where each object is intended to appear + * as a line-item in the version information listing. + * @memberof AboutController# + * @returns {object[]} version information + */ + versions: function () { + return versions; + }, + /** + * Open a new window (or tab, depending on browser + * configuration) containing open source licenses. + * @memberof AboutController# + */ + openLicenses: function () { + // Open a new browser window at the licenses route + $window.open("#/licenses"); + } + }; + } + + return AboutController; + } +); \ No newline at end of file diff --git a/platform/commonUI/about/src/LogoController.js b/platform/commonUI/about/src/LogoController.js new file mode 100644 index 0000000000..1ff6b7602f --- /dev/null +++ b/platform/commonUI/about/src/LogoController.js @@ -0,0 +1,28 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * The LogoController provides functionality to the application + * logo in the bottom-right of the user interface. + * @constructor + * @param {OverlayService} overlayService the overlay service + */ + function LogoController(overlayService) { + return { + /** + * Display the About dialog. + * @memberof LogoController# + */ + showAboutDialog: function () { + overlayService.createOverlay("overlay-about"); + } + }; + } + + return LogoController; + } +); \ No newline at end of file diff --git a/platform/commonUI/about/test/AboutControllerSpec.js b/platform/commonUI/about/test/AboutControllerSpec.js new file mode 100644 index 0000000000..1587b42054 --- /dev/null +++ b/platform/commonUI/about/test/AboutControllerSpec.js @@ -0,0 +1,39 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ['../src/AboutController'], + function (AboutController) { + "use strict"; + + describe("The About controller", function () { + var testVersions, + mockWindow, + controller; + + beforeEach(function () { + testVersions = [ + { name: "Some name", value: "1.2.3" }, + { name: "Some other name", value: "3.2.1" } + ]; + mockWindow = jasmine.createSpyObj("$window", ["open"]); + controller = new AboutController(testVersions, mockWindow); + }); + + it("exposes version information", function () { + // This will be injected, so it should just give back + // what it got in. + expect(controller.versions()).toEqual(testVersions); + }); + + it("opens license information in a window", function () { + //Verify precondition + expect(mockWindow.open).not.toHaveBeenCalled(); + controller.openLicenses(); + expect(mockWindow.open).toHaveBeenCalledWith("#/licenses"); + }); + + + }); + + } +); \ No newline at end of file diff --git a/platform/commonUI/about/test/LogoControllerSpec.js b/platform/commonUI/about/test/LogoControllerSpec.js new file mode 100644 index 0000000000..f3afb2ab57 --- /dev/null +++ b/platform/commonUI/about/test/LogoControllerSpec.js @@ -0,0 +1,32 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ['../src/LogoController'], + function (LogoController) { + "use strict"; + + describe("The About controller", function () { + var mockOverlayService, + controller; + + beforeEach(function () { + mockOverlayService = jasmine.createSpyObj( + "overlayService", + ["createOverlay"] + ); + controller = new LogoController(mockOverlayService); + }); + + it("shows the about dialog", function () { + //Verify precondition + expect(mockOverlayService.createOverlay) + .not.toHaveBeenCalled(); + controller.showAboutDialog(); + expect(mockOverlayService.createOverlay) + .toHaveBeenCalledWith("overlay-about"); + }); + + }); + + } +); \ No newline at end of file diff --git a/platform/commonUI/about/test/suite.json b/platform/commonUI/about/test/suite.json new file mode 100644 index 0000000000..e626ff9b7a --- /dev/null +++ b/platform/commonUI/about/test/suite.json @@ -0,0 +1,4 @@ +[ + "AboutController", + "LogoController" +] \ No newline at end of file diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index feeb0d9e12..ae1c89cc05 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -15,6 +15,16 @@ "templates": [ { "key": "overlay-dialog", + "templateUrl": "templates/overlay-dialog.html" + }, + { + "key": "form-dialog", + "templateUrl": "templates/dialog.html" + } + ], + "containers": [ + { + "key": "overlay", "templateUrl": "templates/overlay.html" } ] diff --git a/platform/commonUI/dialog/res/templates/dialog.html b/platform/commonUI/dialog/res/templates/dialog.html new file mode 100644 index 0000000000..87cf45d2d6 --- /dev/null +++ b/platform/commonUI/dialog/res/templates/dialog.html @@ -0,0 +1,25 @@ +
+
{{ngModel.title}}
+
+ All fields marked * are required. +
+
+
+
+ + +
+
+
+ + OK + + + Cancel + +
\ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-dialog.html b/platform/commonUI/dialog/res/templates/overlay-dialog.html new file mode 100644 index 0000000000..74ec58a075 --- /dev/null +++ b/platform/commonUI/dialog/res/templates/overlay-dialog.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay.html b/platform/commonUI/dialog/res/templates/overlay.html index d5e0d7de51..00636d5d90 100644 --- a/platform/commonUI/dialog/res/templates/overlay.html +++ b/platform/commonUI/dialog/res/templates/overlay.html @@ -3,32 +3,12 @@
x -
-
-
{{ngModel.title}}
-
All fields marked * are required.
-
-
-
- - - -
-
- + ng-if="ngModel.cancel" + class="btn normal outline ui-symbol close"> + x + +
+
\ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index b606c1743e..344a407b94 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -76,8 +76,8 @@ define( // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM overlay = overlayService.createOverlay( - overlayModel, - "overlay-dialog" + "overlay-dialog", + overlayModel ); // Track that a dialog is already visible, to diff --git a/platform/commonUI/dialog/src/OverlayService.js b/platform/commonUI/dialog/src/OverlayService.js index 580595162d..32fd0c54b4 100644 --- a/platform/commonUI/dialog/src/OverlayService.js +++ b/platform/commonUI/dialog/src/OverlayService.js @@ -25,11 +25,21 @@ define( * @constructor */ function OverlayService($document, $compile, $rootScope) { - function createOverlay(overlayModel, key) { + function createOverlay(key, overlayModel) { // Create a new scope for this overlay var scope = $rootScope.$new(), element; + // Stop showing the overlay; additionally, release the scope + // that it uses. + function dismiss() { + scope.$destroy(); + element.remove(); + } + + // If no model is supplied, just fill in a default "cancel" + overlayModel = overlayModel || { cancel: dismiss }; + // Populate the scope; will be passed directly to the template scope.overlay = overlayModel; scope.key = key; @@ -38,12 +48,7 @@ define( element = $compile(TEMPLATE)(scope); $document.find('body').prepend(element); - // Stop showing the overlay; additionally, release the scope - // that it uses. - function dismiss() { - scope.$destroy(); - element.remove(); - } + return { dismiss: dismiss @@ -57,11 +62,12 @@ define( * template (as pointed to by the `key` argument) is * responsible for having a useful z-order, and for * blocking user interactions if appropriate. + * + * @param {string} key the symbolic key which identifies + * the template of the overlay to be shown * @param {object} overlayModel the model to pass to the * included overlay template (this will be passed * in via ng-model) - * @param {string} key the symbolic key which identifies - * the template of the overlay to be shown */ createOverlay: createOverlay }; diff --git a/platform/commonUI/dialog/test/DialogServiceSpec.js b/platform/commonUI/dialog/test/DialogServiceSpec.js index 584e793398..9f3635cd9e 100644 --- a/platform/commonUI/dialog/test/DialogServiceSpec.js +++ b/platform/commonUI/dialog/test/DialogServiceSpec.js @@ -56,7 +56,7 @@ define( it("allows user input to be canceled", function () { dialogService.getUserInput({}, { someKey: "some value" }); - mockOverlayService.createOverlay.mostRecentCall.args[0].cancel(); + mockOverlayService.createOverlay.mostRecentCall.args[1].cancel(); expect(mockDeferred.reject).toHaveBeenCalled(); expect(mockDeferred.resolve).not.toHaveBeenCalled(); }); @@ -64,7 +64,7 @@ define( it("passes back the result of user input when confirmed", function () { var value = { someKey: 42 }; dialogService.getUserInput({}, value); - mockOverlayService.createOverlay.mostRecentCall.args[0].confirm(); + mockOverlayService.createOverlay.mostRecentCall.args[1].confirm(); expect(mockDeferred.reject).not.toHaveBeenCalled(); expect(mockDeferred.resolve).toHaveBeenCalledWith(value); }); @@ -80,7 +80,7 @@ define( it("can show multiple dialogs if prior ones are dismissed", function () { dialogService.getUserInput({}, {}); expect(mockLog.warn).not.toHaveBeenCalled(); - mockOverlayService.createOverlay.mostRecentCall.args[0].confirm(); + mockOverlayService.createOverlay.mostRecentCall.args[1].confirm(); dialogService.getUserInput({}, {}); expect(mockLog.warn).not.toHaveBeenCalled(); expect(mockDeferred.reject).not.toHaveBeenCalled(); diff --git a/platform/commonUI/dialog/test/OverlayServiceSpec.js b/platform/commonUI/dialog/test/OverlayServiceSpec.js index 0adde5a0ea..5506098571 100644 --- a/platform/commonUI/dialog/test/OverlayServiceSpec.js +++ b/platform/commonUI/dialog/test/OverlayServiceSpec.js @@ -8,7 +8,7 @@ define( function (OverlayService) { "use strict"; - describe("The dialog service", function () { + describe("The overlay service", function () { var mockDocument, mockCompile, mockRootScope, @@ -40,19 +40,19 @@ define( }); it("prepends an mct-include to create overlays", function () { - overlayService.createOverlay({}, "test"); + overlayService.createOverlay("test", {}); expect(mockCompile).toHaveBeenCalled(); expect(mockCompile.mostRecentCall.args[0].indexOf("mct-include")) .not.toEqual(-1); }); it("adds the templated element to the body", function () { - overlayService.createOverlay({}, "test"); + overlayService.createOverlay("test", {}); expect(mockBody.prepend).toHaveBeenCalledWith(mockElement); }); it("places the provided model/key in its template's scope", function () { - overlayService.createOverlay({ someKey: 42 }, "test"); + overlayService.createOverlay("test", { someKey: 42 }); expect(mockScope.overlay).toEqual({ someKey: 42 }); expect(mockScope.key).toEqual("test"); @@ -61,7 +61,7 @@ define( }); it("removes the prepended element on request", function () { - var overlay = overlayService.createOverlay({}, "test"); + var overlay = overlayService.createOverlay("test", {}); // Verify precondition expect(mockElement.remove).not.toHaveBeenCalled(); diff --git a/platform/commonUI/general/res/templates/bottombar.html b/platform/commonUI/general/res/templates/bottombar.html index 1036fbc336..e7c326d992 100644 --- a/platform/commonUI/general/res/templates/bottombar.html +++ b/platform/commonUI/general/res/templates/bottombar.html @@ -5,5 +5,5 @@ key="indicator.template"> - + \ No newline at end of file diff --git a/platform/core/bundle.json b/platform/core/bundle.json index 6cb0f329f6..3ae730083c 100644 --- a/platform/core/bundle.json +++ b/platform/core/bundle.json @@ -3,6 +3,19 @@ "description": "Defines core concepts of Open MCT Web.", "sources": "src", "extensions": { + "versions": [ + { + "name": "Open MCT Web", + "value": "0.3.0-dev", + "priority": 1000 + }, + { + "name": "Built", + "value": "YYYY-MM-DDTHH:MM:ssZ", + "description": "The date on which this version of the client was built.", + "priority": 990 + } + ], "components": [ { "provides": "objectService", diff --git a/platform/framework/bundle.json b/platform/framework/bundle.json index a09bea185c..057e9287d0 100644 --- a/platform/framework/bundle.json +++ b/platform/framework/bundle.json @@ -12,5 +12,6 @@ } } }, - "extensions": {} + "extensions": { + } } \ No newline at end of file diff --git a/platform/representation/src/MCTInclude.js b/platform/representation/src/MCTInclude.js index 78ac424180..460fac489b 100644 --- a/platform/representation/src/MCTInclude.js +++ b/platform/representation/src/MCTInclude.js @@ -37,12 +37,14 @@ define( // Prepopulate templateMap for easy look up by key templates.forEach(function (template) { - var path = [ - template.bundle.path, - template.bundle.resources, - template.templateUrl - ].join("/"); - templateMap[template.key] = path; + var key = template.key, + path = [ + template.bundle.path, + template.bundle.resources, + template.templateUrl + ].join("/"); + // First found should win (priority ordering) + templateMap[key] = templateMap[key] || path; }); function controller($scope) {