diff --git a/bundles.json b/bundles.json index 0486bdf24b..6e28332374 100644 --- a/bundles.json +++ b/bundles.json @@ -9,6 +9,7 @@ "platform/commonUI/general", "platform/commonUI/inspect", "platform/containment", + "platform/execution", "platform/telemetry", "platform/features/layout", "platform/features/pages", diff --git a/example/worker/README.md b/example/worker/README.md new file mode 100644 index 0000000000..811539ddeb --- /dev/null +++ b/example/worker/README.md @@ -0,0 +1 @@ +Example of running a Web Worker using the `workerService`. diff --git a/example/worker/bundle.json b/example/worker/bundle.json new file mode 100644 index 0000000000..2241aca2a6 --- /dev/null +++ b/example/worker/bundle.json @@ -0,0 +1,16 @@ +{ + "extensions": { + "indicators": [ + { + "implementation": "FibonacciIndicator.js", + "depends": [ "workerService", "$rootScope" ] + } + ], + "workers": [ + { + "key": "example.fibonacci", + "scriptUrl": "FibonacciWorker.js" + } + ] + } +} diff --git a/example/worker/src/FibonacciIndicator.js b/example/worker/src/FibonacciIndicator.js new file mode 100644 index 0000000000..77a55bc531 --- /dev/null +++ b/example/worker/src/FibonacciIndicator.js @@ -0,0 +1,70 @@ +/***************************************************************************** + * 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"; + + /** + * Displays Fibonacci numbers in the status area. + * @constructor + */ + function FibonacciIndicator(workerService, $rootScope) { + var latest, + counter = 0, + worker = workerService.run('example.fibonacci'); + + function requestNext() { + worker.postMessage([counter]); + counter += 1; + } + + function handleResponse(event) { + latest = event.data; + $rootScope.$apply(); + requestNext(); + } + + worker.onmessage = handleResponse; + requestNext(); + + return { + getGlyph: function () { + return "?"; + }, + getText: function () { + return latest; + }, + getGlyphClass: function () { + return ""; + }, + getDescription: function () { + return ""; + } + }; + } + + return FibonacciIndicator; + } +); diff --git a/example/worker/src/FibonacciWorker.js b/example/worker/src/FibonacciWorker.js new file mode 100644 index 0000000000..2ad8e7f2af --- /dev/null +++ b/example/worker/src/FibonacciWorker.js @@ -0,0 +1,15 @@ +/*global self*/ +(function () { + "use strict"; + + // Calculate fibonacci numbers inefficiently. + // We can do this because we're on a background thread, and + // won't halt the UI. + function fib(n) { + return n < 2 ? n : (fib(n - 1) + fib(n - 2)); + } + + self.onmessage = function (event) { + self.postMessage(fib(event.data)); + }; +}()); diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index 00bb27d430..ef43a9da06 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -4322,51 +4322,55 @@ input[type="text"] { * at runtime from the About dialog for additional information. *****************************************************************************/ /* line 24, ../sass/helpers/_bubbles.scss */ +.bubble-container { + pointer-events: none; } + +/* line 31, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper { -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; position: relative; z-index: 50; } - /* line 29, ../sass/helpers/_bubbles.scss */ + /* line 36, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble { display: inline-block; min-width: 100px; max-width: 300px; padding: 5px 10px; } - /* line 34, ../sass/helpers/_bubbles.scss */ + /* line 41, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble:before { content: ""; position: absolute; width: 0; height: 0; } - /* line 40, ../sass/helpers/_bubbles.scss */ + /* line 47, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble table { width: 100%; } - /* line 43, ../sass/helpers/_bubbles.scss */ + /* line 50, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble table tr td { padding: 2px 0; vertical-align: top; } - /* line 50, ../sass/helpers/_bubbles.scss */ + /* line 57, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble table tr td.label { padding-right: 10px; white-space: nowrap; } - /* line 54, ../sass/helpers/_bubbles.scss */ + /* line 61, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble table tr td.value { white-space: nowrap; } - /* line 58, ../sass/helpers/_bubbles.scss */ + /* line 65, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble table tr td.align-wrap { white-space: normal; } - /* line 64, ../sass/helpers/_bubbles.scss */ + /* line 71, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 5px; } - /* line 71, ../sass/helpers/_bubbles.scss */ + /* line 78, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-left { margin-left: 20px; } - /* line 73, ../sass/helpers/_bubbles.scss */ + /* line 80, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-left .l-infobubble::before { right: 100%; width: 0; @@ -4374,10 +4378,10 @@ input[type="text"] { border-top: 6.66667px solid transparent; border-bottom: 6.66667px solid transparent; border-right: 10px solid #ddd; } - /* line 79, ../sass/helpers/_bubbles.scss */ + /* line 86, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-right { margin-right: 20px; } - /* line 81, ../sass/helpers/_bubbles.scss */ + /* line 88, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-right .l-infobubble::before { left: 100%; width: 0; @@ -4385,16 +4389,16 @@ input[type="text"] { border-top: 6.66667px solid transparent; border-bottom: 6.66667px solid transparent; border-left: 10px solid #ddd; } - /* line 88, ../sass/helpers/_bubbles.scss */ + /* line 95, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-top .l-infobubble::before { top: 20px; } - /* line 94, ../sass/helpers/_bubbles.scss */ + /* line 101, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-btm .l-infobubble::before { bottom: 20px; } - /* line 99, ../sass/helpers/_bubbles.scss */ + /* line 106, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-down { margin-bottom: 10px; } - /* line 101, ../sass/helpers/_bubbles.scss */ + /* line 108, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-down .l-infobubble::before { left: 50%; top: 100%; @@ -4402,21 +4406,21 @@ input[type="text"] { border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 7.5px solid #ddd; } - /* line 110, ../sass/helpers/_bubbles.scss */ + /* line 117, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .arw { z-index: 2; } - /* line 113, ../sass/helpers/_bubbles.scss */ + /* line 120, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-up .arw.arw-down, .l-infobubble-wrapper.arw-down .arw.arw-up { display: none; } -/* line 120, ../sass/helpers/_bubbles.scss */ +/* line 127, ../sass/helpers/_bubbles.scss */ .l-thumbsbubble-wrapper .arw-up { width: 0; height: 0; border-left: 6.66667px solid transparent; border-right: 6.66667px solid transparent; border-bottom: 10px solid #4d4d4d; } -/* line 123, ../sass/helpers/_bubbles.scss */ +/* line 130, ../sass/helpers/_bubbles.scss */ .l-thumbsbubble-wrapper .arw-down { width: 0; height: 0; @@ -4424,7 +4428,7 @@ input[type="text"] { border-right: 6.66667px solid transparent; border-top: 10px solid #4d4d4d; } -/* line 127, ../sass/helpers/_bubbles.scss */ +/* line 134, ../sass/helpers/_bubbles.scss */ .s-infobubble { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -4435,22 +4439,22 @@ input[type="text"] { background: #ddd; color: #666; font-size: 0.8rem; } - /* line 134, ../sass/helpers/_bubbles.scss */ + /* line 141, ../sass/helpers/_bubbles.scss */ .s-infobubble .title { color: #333333; font-weight: bold; } - /* line 139, ../sass/helpers/_bubbles.scss */ + /* line 146, ../sass/helpers/_bubbles.scss */ .s-infobubble tr td { border-top: 1px solid #c4c4c4; font-size: 0.9em; } - /* line 143, ../sass/helpers/_bubbles.scss */ + /* line 150, ../sass/helpers/_bubbles.scss */ .s-infobubble tr:first-child td { border-top: none; } - /* line 147, ../sass/helpers/_bubbles.scss */ + /* line 154, ../sass/helpers/_bubbles.scss */ .s-infobubble .value { color: #333333; } -/* line 152, ../sass/helpers/_bubbles.scss */ +/* line 159, ../sass/helpers/_bubbles.scss */ .s-thumbsbubble { background: #4d4d4d; color: #b3b3b3; } diff --git a/platform/commonUI/general/res/sass/helpers/_bubbles.scss b/platform/commonUI/general/res/sass/helpers/_bubbles.scss index e9648523c4..5b174ba6da 100644 --- a/platform/commonUI/general/res/sass/helpers/_bubbles.scss +++ b/platform/commonUI/general/res/sass/helpers/_bubbles.scss @@ -19,6 +19,13 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ + +//************************************************* GENERAL +.bubble-container { + pointer-events: none; +} + + //************************************************* LAYOUT .l-infobubble-wrapper { diff --git a/platform/commonUI/inspect/bundle.json b/platform/commonUI/inspect/bundle.json index 07506d0983..51244b2bf2 100644 --- a/platform/commonUI/inspect/bundle.json +++ b/platform/commonUI/inspect/bundle.json @@ -44,7 +44,7 @@ "constants": [ { "key": "INFO_HOVER_DELAY", - "value": 500 + "value": 2000 } ] } diff --git a/platform/commonUI/inspect/res/info-bubble.html b/platform/commonUI/inspect/res/info-bubble.html index 1deeeade15..82545cb29e 100644 --- a/platform/commonUI/inspect/res/info-bubble.html +++ b/platform/commonUI/inspect/res/info-bubble.html @@ -1,6 +1,8 @@ - + diff --git a/platform/commonUI/inspect/src/InfoConstants.js b/platform/commonUI/inspect/src/InfoConstants.js index 86570911c6..5e43a1b618 100644 --- a/platform/commonUI/inspect/src/InfoConstants.js +++ b/platform/commonUI/inspect/src/InfoConstants.js @@ -23,7 +23,8 @@ define({ BUBBLE_TEMPLATE: "" + + "bubble-layout=\"{{bubbleLayout}}\" " + + "class=\"bubble-container\">" + "" + "" + "", diff --git a/platform/execution/README.md b/platform/execution/README.md new file mode 100644 index 0000000000..2188e5f909 --- /dev/null +++ b/platform/execution/README.md @@ -0,0 +1 @@ +Contains services which manage execution and flow control (e.g. for concurrency.) diff --git a/platform/execution/bundle.json b/platform/execution/bundle.json new file mode 100644 index 0000000000..6e6ea83eee --- /dev/null +++ b/platform/execution/bundle.json @@ -0,0 +1,11 @@ +{ + "extensions": { + "services": [ + { + "key": "workerService", + "implementation": "WorkerService.js", + "depends": [ "$window", "workers[]" ] + } + ] + } +} diff --git a/platform/execution/src/WorkerService.js b/platform/execution/src/WorkerService.js new file mode 100644 index 0000000000..b8f24ee614 --- /dev/null +++ b/platform/execution/src/WorkerService.js @@ -0,0 +1,68 @@ +/***************************************************************************** + * 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"; + + /** + * Handles the execution of WebWorkers. + * @constructor + */ + function WorkerService($window, workers) { + var workerUrls = {}, + Worker = $window.Worker; + + function addWorker(worker) { + var key = worker.key; + if (!workerUrls[key]) { + workerUrls[key] = [ + worker.bundle.path, + worker.bundle.sources, + worker.scriptUrl + ].join("/"); + } + } + + (workers || []).forEach(addWorker); + + return { + /** + * Start running a new web worker. This will run a worker + * that has been registered under the `workers` category + * of extension. + * + * @param {string} key symbolic identifier for the worker + * @returns {Worker} the running Worker + */ + run: function (key) { + var scriptUrl = workerUrls[key]; + return scriptUrl && Worker && new Worker(scriptUrl); + } + }; + } + + return WorkerService; + } +); diff --git a/platform/execution/test/WorkerServiceSpec.js b/platform/execution/test/WorkerServiceSpec.js new file mode 100644 index 0000000000..24abab6e81 --- /dev/null +++ b/platform/execution/test/WorkerServiceSpec.js @@ -0,0 +1,77 @@ +/***************************************************************************** + * 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,describe,it,expect,beforeEach,jasmine*/ + +define( + ["../src/WorkerService"], + function (WorkerService) { + "use strict"; + + describe("The worker service", function () { + var mockWindow, + testWorkers, + mockWorker, + service; + + beforeEach(function () { + mockWindow = jasmine.createSpyObj('$window', ['Worker']); + testWorkers = [ + { + key: 'abc', + scriptUrl: 'c.js', + bundle: { path: 'a', sources: 'b' } + }, + { + key: 'xyz', + scriptUrl: 'z.js', + bundle: { path: 'x', sources: 'y' } + }, + { + key: 'xyz', + scriptUrl: 'bad.js', + bundle: { path: 'bad', sources: 'bad' } + } + ]; + mockWorker = {}; + + mockWindow.Worker.andReturn(mockWorker); + + service = new WorkerService(mockWindow, testWorkers); + }); + + it("instantiates workers at registered paths", function () { + expect(service.run('abc')).toBe(mockWorker); + expect(mockWindow.Worker).toHaveBeenCalledWith('a/b/c.js'); + }); + + it("prefers the first worker when multiple keys are found", function () { + expect(service.run('xyz')).toBe(mockWorker); + expect(mockWindow.Worker).toHaveBeenCalledWith('x/y/z.js'); + }); + + it("returns undefined for unknown workers", function () { + expect(service.run('def')).toBeUndefined(); + }); + + }); + } +); diff --git a/platform/execution/test/suite.json b/platform/execution/test/suite.json new file mode 100644 index 0000000000..d14a0714c5 --- /dev/null +++ b/platform/execution/test/suite.json @@ -0,0 +1,3 @@ +[ + "WorkerService" +]