diff --git a/.gitignore b/.gitignore index aacf7f9728..b8eea4a5be 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.gzip *.tgz *.DS_Store +*.swp # Compiled CSS, unless directly added *.sass-cache diff --git a/README.md b/README.md index e2d3a979d9..b9486dad3c 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,15 @@ Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting S Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/). ![Demo](https://nasa.github.io/openmct/static/res/images/Open-MCT.Browse.Layout.Mars-Weather-1.jpg) +## New API +A new API is currently under development that will deprecate a lot of the documentation currently in the docs directory, however Open MCT will remain compatible with the currently documented API. An updated set of tutorials is being developed with the new API, and progress on this task can be followed in the [associated pull request](https://github.com/nasa/openmct/pull/999). Any code in this branch should be considered experimental, and we welcome any feedback. + +Differences between the two APIs include a move away from a declarative system of JSON configuration files towards an imperative system based on function calls. Developers will be able to extend and build on Open MCT by making direct function calls to a public API. Open MCT is also being refactored to minimize the dependencies that using Open MCT imposes on developers, such as the current requirement to use Angular JS. + ## Building and Running Open MCT Locally Building and running Open MCT in your local dev environment is very easy. Be sure you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed, then follow the directions below. Need additional information? Check out the [Getting Started](https://nasa.github.io/openmct/getting-started/) page on our website. +(These instructions assume you are installing as a non-root user; developers have [reported issues](https://github.com/nasa/openmct/issues/1151) running these steps with root privileges.) 1. Clone the source code @@ -31,6 +37,22 @@ Open MCT is now running, and can be accessed by pointing a web browser at [http: Documentation is available on the [Open MCT website](https://nasa.github.io/openmct/documentation/). The documentation can also be built locally. +### Examples + +The clearest examples for developing Open MCT plugins are in the +[tutorials](https://nasa.github.io/openmct/docs/tutorials/) provided in +our documentation. + +For a practical example of a telemetry adapter, see David Hudson's +[Kerbal Space Program plugin](https://github.com/hudsonfoo/kerbal-openmct), +which allows [Kerbal Space Program](https://kerbalspaceprogram.com) players +to build and use displays for their own missions in Open MCT. + +Additional examples are available in the `examples` hierarchy of this +repository; however, be aware that these examples are +[not fully-documented](https://github.com/nasa/openmct/issues/846), so +the tutorials will likely serve as a better starting point. + ### Building the Open MCT Documentation Locally Open MCT's documentation is generated by an [npm](https://www.npmjs.com/)-based build. It has additional dependencies that diff --git a/bower.json b/bower.json index 7c913754cf..419871fe57 100644 --- a/bower.json +++ b/bower.json @@ -13,11 +13,12 @@ "moment-duration-format": "^1.3.0", "requirejs": "~2.1.22", "text": "requirejs-text#^2.0.14", - "es6-promise": "^3.0.2", + "es6-promise": "^3.3.0", "screenfull": "^3.0.0", "node-uuid": "^1.4.7", "comma-separated-values": "^3.6.4", "FileSaver.js": "^0.0.2", - "zepto": "^1.1.6" + "zepto": "^1.1.6", + "html2canvas": "^0.4.1" } } diff --git a/build-docs.sh b/build-docs.sh index ca7dc97c2e..29841f921e 100755 --- a/build-docs.sh +++ b/build-docs.sh @@ -32,7 +32,7 @@ WEBSITE_DIRECTORY="website" BUILD_SHA=`git rev-parse HEAD` # A remote will be created for the git repository we are pushing to. -# Don't worry, as this entire directory will get trashed inbetween builds. +# Don't worry, as this entire directory will get trashed in between builds. REMOTE_NAME="documentation" WEBSITE_BRANCH="master" @@ -45,8 +45,8 @@ npm run docs echo "git clone $REPOSITORY_URL website" git clone $REPOSITORY_URL website || exit 1 -echo "cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY/docs" -cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY/docs +echo "cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY" +cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY echo "cd $WEBSITE_DIRECTORY" cd $WEBSITE_DIRECTORY || exit 1 diff --git a/docs/src/architecture/index.md b/docs/src/architecture/index.md index a4586a3542..6f2fbcfcef 100644 --- a/docs/src/architecture/index.md +++ b/docs/src/architecture/index.md @@ -6,7 +6,7 @@ overall architecture of Open MCT. The target audience includes: * _Platform maintainers_: Individuals involved in developing, - extending, and maintaing capabilities of the platform. + extending, and maintaining capabilities of the platform. * _Integration developers_: Individuals tasked with integrated Open MCT into a larger system, who need to understand its inner workings sufficiently to complete this integration. @@ -63,7 +63,7 @@ These layers are: application-specific knowledge; at this layer, we have only established an abstraction by which different software components may communicate and/or interact. -* [_Platform_](platform.md): The platform layer defines the general look, +* [_Platform_](platform.md): The platform layer defines the general look, feel, and behavior of Open MCT. This includes user-facing components like Browse mode and Edit mode, as well as underlying elements of the information model and the general service infrastructure. @@ -74,5 +74,3 @@ These layers are: typically consists of a mix of custom plug-ins to Open MCT, as well as optional features (such as Plot view) included alongside the platform. - - diff --git a/docs/src/architecture/platform.md b/docs/src/architecture/platform.md index 1f5e087a11..48ceebb9ef 100644 --- a/docs/src/architecture/platform.md +++ b/docs/src/architecture/platform.md @@ -1,6 +1,6 @@ # Overview -The Open MCT platform utilizes the [framework layer](Framework.md) +The Open MCT platform utilizes the [framework layer](framework.md) to provide an extensible baseline for applications which includes: * A common user interface (and user interface paradigm) for dealing with @@ -38,7 +38,7 @@ in __any of these tiers__. are initiated from here and invoke behavior in the presentation layer. HTML  templates are written in Angular’s template syntax; see the [Angular documentation on templates](https://docs.angularjs.org/guide/templates)​.  These describe the page as actually seen by the user. Conceptually,  - stylesheets (controlling the look­and­feel of the rendered templates) belong  + stylesheets (controlling the look-and-feel of the rendered templates) belong  in this grouping as well.  * [_Presentation layer_](#presentation-layer): The presentation layer is responsible for updating (and providing information to update) @@ -48,7 +48,7 @@ in __any of these tiers__. display. * [_Information model_](#information-model): ​Provides a common (within Open MCT  Web) set of interfaces for dealing with “things” ­ domain objects ­ within the  - system. User­facing concerns in a Open MCT Web application are expressed as  + system. User-facing concerns in a Open MCT Web application are expressed as  domain objects; examples include folders (used to organize other domain  objects), layouts (used to build displays), or telemetry points (used as  handles for streams of remote measurements.) These domain objects expose a  @@ -616,7 +616,7 @@ follows: part of an action's extension definition. * `CreateActionProvider` provides the various Create actions which populate the Create menu. These are driven by the available types, - so do not map easily ot extension category `actions`; instead, these + so do not map easily to extension category `actions`; instead, these are generated after looking up which actions are available from the [`TypeService`](#type-service). * `ActionAggregator` merges together actions from multiple providers. diff --git a/docs/src/design/proposals/APIRedesign.md b/docs/src/design/proposals/APIRedesign.md index d14f4ce469..03ce2f212f 100644 --- a/docs/src/design/proposals/APIRedesign.md +++ b/docs/src/design/proposals/APIRedesign.md @@ -98,7 +98,7 @@ Worked on bug fixes in the platform and a plugin for search. It is hard to figure out what the difference between the various ways of dealing with telemetry are. e.g., what is the difference between just "Telemetry" and the "Telemetry Service"? There are many - "Telemetry Thing"s which seem related, but in an unclear way. + "Telemetry Things" which seem related, but in an unclear way. ### Developer Intern 2 @@ -180,7 +180,7 @@ to develop a tabular visualization plugin. * Add a model property to the bundle.json to take in "Hello World" as a parameter and pass through to the controller/view -### Open Source Contributer +### Open Source Contributor * [Failures are non-graceful when services are missing.]( https://github.com/nasa/openmctweb/issues/79) @@ -214,7 +214,7 @@ to an entirely different framework. We can expect AngularJS 1.x to reach end-of-life reasonably soon thereafter. -Our API is currently a superset of Angular's API, so this directly effects +Our API is currently a superset of Angular's API, so this directly affects our API. Specifically, API changes should be oriented towards removing or reducing the Angular dependency. @@ -456,7 +456,7 @@ Instead, propose that: For parity with actions, a `View` would be a constructor which takes an `ActionContext` as a parameter (with similarly-defined properties) and exposes a method to retrieve the HTML elements -associateed with it. +associated with it. The platform would then additionally expose an `AngularView` implementation to improve compatibility with existing diff --git a/docs/src/design/proposals/APIRedesign_PeteRichards.md b/docs/src/design/proposals/APIRedesign_PeteRichards.md index 3fa78fe2ed..517d61cc50 100644 --- a/docs/src/design/proposals/APIRedesign_PeteRichards.md +++ b/docs/src/design/proposals/APIRedesign_PeteRichards.md @@ -3,7 +3,7 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Reducing interface depth (the bundle.json version)](#reducing-interface-depth-the-bundlejson-version) - - [Imperitive component registries](#imperitive-component-registries) + - [Imperitive component registries](#imperative-component-registries) - [Get rid of "extension category" concept.](#get-rid-of-extension-category-concept) - [Reduce number and depth of extension points](#reduce-number-and-depth-of-extension-points) - [Composite services should not be the default](#composite-services-should-not-be-the-default) @@ -30,11 +30,11 @@ # Reducing interface depth (the bundle.json version) -## Imperitive component registries +## Imperative component registries Transition component registries to javascript, get rid of bundle.json and bundles.json. Prescribe a method for application configuration, but allow flexibility in how application configuration is defined. -Register components in an imperitive fashion, see angularApp.factory, angularApp.controller, etc. Alternatively, implement our own application object with new registries and it's own form of registering objects. +Register components in an imperative fashion, see angularApp.factory, angularApp.controller, etc. Alternatively, implement our own application object with new registries and it's own form of registering objects. ## Get rid of "extension category" concept. @@ -99,7 +99,7 @@ To reduce interface depth, we can replace our own provider and registry patterns ## More angular: for all services -Increasing our commitment to angular would mean using more of the angular factorys, services, etc, and less of our home grown tools. We'd implement our services and extension points as angular providers, and make them configurable via app.config. +Increasing our commitment to angular would mean using more of the angular factories, services, etc, and less of our home grown tools. We'd implement our services and extension points as angular providers, and make them configurable via app.config. As an example, registering a specific type of model provider in angular would look like: @@ -126,9 +126,9 @@ Allow developers to use whatever module loading system they'd like to use, while ## Use gulp or grunt for standard tooling -Using gulp or grunt as a task runner would bring us in line with standard web developer workflows and help standardize rendering, deployment, and packaging. Additional tools can be added to the workflow at low cost, simplifying the set up of developer environments. +Using gulp or grunt as a task runner would bring us in line with standard web developer workflows and help standardize rendering, deployment, and packaging. Additional tools can be added to the workflow at low cost, simplifying the setup of developer environments. -Gulp and grunt provide useful developer tooling such as live reload, automatic scss/less/etc compiliation, and ease of extensibility for standard production build processes. They're key in decoupling code. +Gulp and grunt provide useful developer tooling such as live reload, automatic scss/less/etc compilation, and ease of extensibility for standard production build processes. They're key in decoupling code. ## Package openmctweb as single versioned file. diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 081f1df45a..1d8a422299 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -643,7 +643,7 @@ to be passed along by other services. ## Domain Objects Domain objects are the most fundamental component of Open MCT's information -model. A domain object is some distinct thing relevant to a user's work flow, +model. A domain object is some distinct thing relevant to a user's workflow, such as a telemetry channel, display, or similar. Open MCT is a tool for viewing, browsing, manipulating, and otherwise interacting with a graph of domain objects. @@ -933,7 +933,7 @@ Note that `templateUrl` is not supported for `containers`. Controls provide options for the `mct-control` directive. -Six standard control types are included in the forms bundle: +Ten standard control types are included in the forms bundle: * `textfield`: An area to enter plain text. * `select`: A drop-down list of options. @@ -941,7 +941,13 @@ Six standard control types are included in the forms bundle: * `color`: A color picker. * `button`: A button. * `datetime`: An input for UTC date/time entry; gives result as a UNIX -timestamp, in milliseconds since start of 1970, UTC. +timestamp, in milliseconds since start of 1970, UTC. +* `composite`: A control parenting an array of other controls. +* `menu-button`: A drop-down list of items supporting custom behavior +on click. +* `dialog-button`: A button which opens a dialog allowing a single property +to be edited. +* `radio`: A radio button. New controls may be added as extensions of the controls category. Extensions of this category have two properties: @@ -981,7 +987,7 @@ Examples of gestures included in the platform are: composition. * `drop`: For representations that can be drop targets for drag-and-drop composition. -* `menu`: For representations that can be used to pop up a context menu. +* `menu`: For representations that can be used to popup a context menu. Gesture definitions have a property `key` which is used as a machine-readable identifier for the gesture (e.g. `drag`, `drop`, `menu` above.) @@ -1153,7 +1159,7 @@ For example, the _My Items_ folder is added as an extension of this category. Extensions of this category should have the following properties: -* `id`: The machine-readable identifier for the domaiwn object being exposed. +* `id`: The machine-readable identifier for the domain object being exposed. * `model`: The model, as a JSON object, for the domain object being exposed. ## Stylesheets Category diff --git a/docs/src/process/testing/plan.md b/docs/src/process/testing/plan.md index 47ab60ee34..34e75855c9 100644 --- a/docs/src/process/testing/plan.md +++ b/docs/src/process/testing/plan.md @@ -102,7 +102,7 @@ perform: * A relevant subset of [_user testing_](procedures.md#user-test-procedures) identified by the acting [project manager](../cycle.md#roles). -* [_Long-duration testing_](procedures.md#long-duration-testng) +* [_Long-duration testing_](procedures.md#long-duration-testing) (specifically, for 24 hours.) Issues are reported as a product of both forms of testing. diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 84c571bec8..d8842de9d7 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -48,17 +48,17 @@ for a more general overview of how to run and deploy a Open MCT application. First step is to check out Open MCT from the source repository. -`git clone https://github.com/nasa/openmctweb.git openmctweb` +`git clone https://github.com/nasa/openmct.git openmct` This will create a copy of the Open MCT source code repository in the folder -`openmctweb` (relative to the path from which you ran the command.) +`openmct` (relative to the path from which you ran the command.) If you have a repository URL, use that as the "path to repo" above. Alternately, if you received Open MCT as a git bundle, the path to that bundle on the local filesystem can be used instead. At this point, it will also be useful to branch off of Open MCT v0.6.2 (which was used when writing these tutorials) to begin adding plugins. - cd openmctweb + cd openmct git branch open-v0.6.2 git checkout @@ -79,7 +79,7 @@ changes to stylesheets, or you are running the minified version of the app The next step is to run a web server so that you can view the Open MCT client (including the plugins you add to it) in browser. Any web server can -be used for hosting OpenMCTWeb, and a trivial web server is provided in this +be used for hosting Open MCT, and a trivial web server is provided in this package for the purposes of running the tutorials. The provided web server should not be used in a production environment @@ -91,7 +91,7 @@ To run the tutorial web server Once running, you should be able to view Open MCT from your browser at http://localhost:8080/ (assuming the web server is running on port 8080, -and OpenMCTWeb is installed at the server's root path). +and Open MCT is installed at the server's root path). [Google Chrome](https://www.google.com/chrome/) is recommended for these tutorials, as Chrome is Open MCT's "test-to" browser. The browser cache can sometimes interfere with development (masking changes by @@ -130,7 +130,6 @@ to this plugin as tutorials/todo as well.) We will start with an "empty bundle", one which exposes no extensions - which looks like: ```diff - define([ 'legacyRegistry' ], function ( @@ -144,7 +143,6 @@ define([ } }); }); - ``` __tutorials/todo/bundle.js__ @@ -348,7 +346,8 @@ deeper explanation of domain objects, see the Open MCT Developer Guide.) In the case of our to-do list feature, the to-do list itself is the thing we'll want users to be able to create and edit. So, we will add that as a new type in our bundle definition: -```diff + +```diff define([ 'legacyRegistry' ], function ( @@ -370,7 +369,6 @@ define([ + ]} }); }); - ``` __tutorials/todo/bundle.js__ @@ -427,7 +425,6 @@ are stored by convention.) ``` - __tutorials/todo/res/templates/todo.html__ A summary of what's included: @@ -573,6 +570,7 @@ We will define that in an AMD module (see http://requirejs.org/docs/whyamd.html) in the directory `tutorials/todo/src/controllers` (`src` is, by default, the directory where bundle-related source code is kept, and controllers is where Angular controllers are stored by convention.) + ```diff define(function () { function TodoController($scope) { @@ -971,6 +969,7 @@ by the tool bar we've defined. Additionally, we need to make changes to our template to select specific tasks in response to some user gesture. Here, we will select tasks when a user clicks the description. + ```diff
@@ -996,6 +995,7 @@ __tutorials/todo/res/templates/todo.html__ Finally, the `TodoController` uses the `dialogService` now, so we need to declare that dependency in its extension definition: + ```diff define([ 'legacyRegistry', @@ -1248,7 +1248,6 @@ another file to the res directory of our bundle; this time, it is `css/todo.css` font-style: italic; } ``` - __tutorials/todo/res/css/todo.css__ Here, we have defined classes and appearances for: @@ -1261,6 +1260,7 @@ Here, we have defined classes and appearances for: To include this CSS file in our running instance of Open MCT, we need to declare it in our bundle definition, this time as an extension of category `stylesheets`: + ```diff define([ 'legacyRegistry', @@ -1430,7 +1430,6 @@ define([ }); }); ``` - __tutorials/bargraph/bundle.js__ The view definition should look familiar after the To-Do List tutorial, with @@ -1499,6 +1498,7 @@ The third is for labels along the horizontal axis, which will indicate which bar corresponds to which telemetry point. Inline `style` attributes are used wherever dynamic positioning (handled by a script) is anticipated. The corresponding CSS file which styles and positions these elements: + ```diff .example-bargraph { position: absolute; @@ -1596,6 +1596,7 @@ actual telemetry data in subsequent steps.) Notably, we will not try to show telemetry data after this step. To support this, we will add a new controller which supports our Bar Graph view: + ```diff define(function () { function BarGraphController($scope, telemetryHandler) { @@ -1647,6 +1648,7 @@ Whenever the telemetry handler invokes its callbacks, we update the set of telemetry objects in view, as well as the width for each bar. We will also utilize this from our template: + ```diff +
@@ -2346,6 +2348,7 @@ add a top-level object which will serve as a container; in the next step, we will populate this with the contents of the telemetry dictionary (which we will retrieve from the server.) +```diff define([ 'legacyRegistry' ], function ( @@ -2375,6 +2378,7 @@ define([ } }); }); +``` __tutorials/telemetry/bundle.js__ Here, we've created our initial telemetry plugin. This exposes a new domain @@ -2469,7 +2473,6 @@ define([ }; }); ``` - __main.js__ ...we will be able to reload Open MCT and see that it is present: @@ -2486,43 +2489,45 @@ server. Our first step will be to add a service that will handle interactions with the server; this will not be used by Open MCT directly, but will be used by subsequent components we add. - /*global define,WebSocket*/ - - define( - [], - function () { - "use strict"; - - function ExampleTelemetryServerAdapter($q, wsUrl) { - var ws = new WebSocket(wsUrl), - dictionary = $q.defer(); - - // Handle an incoming message from the server - ws.onmessage = function (event) { - var message = JSON.parse(event.data); - - switch (message.type) { - case "dictionary": - dictionary.resolve(message.value); - break; - } - }; - - // Request dictionary once connection is established - ws.onopen = function () { - ws.send("dictionary"); - }; - - return { - dictionary: function () { - return dictionary.promise; - } - }; - } - - return ExampleTelemetryServerAdapter; +```diff +/*global define,WebSocket*/ + +define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + } + }; } - ); + + return ExampleTelemetryServerAdapter; + } +); +``` __tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ When created, this service initiates a connection to the server, and begins @@ -2539,86 +2544,88 @@ subsystems. This means that we need to convert the data from the dictionary into domain object models, and expose these to Open MCT via a `modelService`. - /*global define*/ - - define( - function () { - "use strict"; - - var PREFIX = "example_tlm:", - FORMAT_MAPPINGS = { - float: "number", - integer: "number", - string: "string" - }; - - function ExampleTelemetryModelProvider(adapter, $q) { - var modelPromise, empty = $q.when({}); - - // Check if this model is in our dictionary (by prefix) - function isRelevant(id) { - return id.indexOf(PREFIX) === 0; - } - - // Build a domain object identifier by adding a prefix - function makeId(element) { - return PREFIX + element.identifier; - } - - // Create domain object models from this dictionary - function buildTaxonomy(dictionary) { - var models = {}; - - // Create & store a domain object model for a measurement - function addMeasurement(measurement) { - var format = FORMAT_MAPPINGS[measurement.type]; - models[makeId(measurement)] = { - type: "example.measurement", - name: measurement.name, - telemetry: { - key: measurement.identifier, - ranges: [{ - key: "value", - name: "Value", - units: measurement.units, - format: format - }] - } - }; - } - - // Create & store a domain object model for a subsystem - function addSubsystem(subsystem) { - var measurements = - (subsystem.measurements || []); - models[makeId(subsystem)] = { - type: "example.subsystem", - name: subsystem.name, - composition: measurements.map(makeId) - }; - measurements.forEach(addMeasurement); - } - - (dictionary.subsystems || []).forEach(addSubsystem); - - return models; - } - - // Begin generating models once the dictionary is available - modelPromise = adapter.dictionary().then(buildTaxonomy); - - return { - getModels: function (ids) { - // Return models for the dictionary only when they - // are relevant to the request. - return ids.some(isRelevant) ? modelPromise : empty; - } - }; +```diff +/*global define*/ + +define( + function () { + "use strict"; + + var PREFIX = "example_tlm:", + FORMAT_MAPPINGS = { + float: "number", + integer: "number", + string: "string" + }; + + function ExampleTelemetryModelProvider(adapter, $q) { + var modelPromise, empty = $q.when({}); + + // Check if this model is in our dictionary (by prefix) + function isRelevant(id) { + return id.indexOf(PREFIX) === 0; } - - return ExampleTelemetryModelProvider; + + // Build a domain object identifier by adding a prefix + function makeId(element) { + return PREFIX + element.identifier; + } + + // Create domain object models from this dictionary + function buildTaxonomy(dictionary) { + var models = {}; + + // Create & store a domain object model for a measurement + function addMeasurement(measurement) { + var format = FORMAT_MAPPINGS[measurement.type]; + models[makeId(measurement)] = { + type: "example.measurement", + name: measurement.name, + telemetry: { + key: measurement.identifier, + ranges: [{ + key: "value", + name: "Value", + units: measurement.units, + format: format + }] + } + }; + } + + // Create & store a domain object model for a subsystem + function addSubsystem(subsystem) { + var measurements = + (subsystem.measurements || []); + models[makeId(subsystem)] = { + type: "example.subsystem", + name: subsystem.name, + composition: measurements.map(makeId) + }; + measurements.forEach(addMeasurement); + } + + (dictionary.subsystems || []).forEach(addSubsystem); + + return models; + } + + // Begin generating models once the dictionary is available + modelPromise = adapter.dictionary().then(buildTaxonomy); + + return { + getModels: function (ids) { + // Return models for the dictionary only when they + // are relevant to the request. + return ids.some(isRelevant) ? modelPromise : empty; + } + }; } - ); + + return ExampleTelemetryModelProvider; + } +); +``` __tutorials/telemetry/src/ExampleTelemetryModelProvider.js__ This script implements a `provider` for `modelService`; the `modelService` is a @@ -2671,55 +2678,57 @@ This allows our telemetry dictionary to be expressed as domain object models fix this, we will need another script which will add these subsystems to the root-level object we added in Step 1. - /*global define*/ - - define( - function () { - "use strict"; - - var TAXONOMY_ID = "example:sc", - PREFIX = "example_tlm:"; - - function ExampleTelemetryInitializer(adapter, objectService) { - // Generate a domain object identifier for a dictionary element - function makeId(element) { - return PREFIX + element.identifier; - } - - // When the dictionary is available, add all subsystems - // to the composition of My Spacecraft - function initializeTaxonomy(dictionary) { - // Get the top-level container for dictionary objects - // from a group of domain objects. - function getTaxonomyObject(domainObjects) { - return domainObjects[TAXONOMY_ID]; - } - - // Populate - function populateModel(taxonomyObject) { - return taxonomyObject.useCapability( - "mutation", - function (model) { - model.name = - dictionary.name; - model.composition = - dictionary.subsystems.map(makeId); - } - ); - } - - // Look up My Spacecraft, and populate it accordingly. - objectService.getObjects([TAXONOMY_ID]) - .then(getTaxonomyObject) - .then(populateModel); - } - - adapter.dictionary().then(initializeTaxonomy); +```diff +/*global define*/ + +define( + function () { + "use strict"; + + var TAXONOMY_ID = "example:sc", + PREFIX = "example_tlm:"; + + function ExampleTelemetryInitializer(adapter, objectService) { + // Generate a domain object identifier for a dictionary element + function makeId(element) { + return PREFIX + element.identifier; } - - return ExampleTelemetryInitializer; + + // When the dictionary is available, add all subsystems + // to the composition of My Spacecraft + function initializeTaxonomy(dictionary) { + // Get the top-level container for dictionary objects + // from a group of domain objects. + function getTaxonomyObject(domainObjects) { + return domainObjects[TAXONOMY_ID]; + } + + // Populate + function populateModel(taxonomyObject) { + return taxonomyObject.useCapability( + "mutation", + function (model) { + model.name = + dictionary.name; + model.composition = + dictionary.subsystems.map(makeId); + } + ); + } + + // Look up My Spacecraft, and populate it accordingly. + objectService.getObjects([TAXONOMY_ID]) + .then(getTaxonomyObject) + .then(populateModel); + } + + adapter.dictionary().then(initializeTaxonomy); } - ); + + return ExampleTelemetryInitializer; + } +); +``` __tutorials/telemetry/src/ExampleTelemetryInitializer.js__ At the conclusion of Step 1, the top-level My Spacecraft object was empty. This @@ -2737,14 +2746,14 @@ with the platform): ```diff define([ 'legacyRegistry', - './src/ExampleTelemetryServerAdapter', - './src/ExampleTelemetryInitializer', - './src/ExampleTelemetryModelProvider' ++ './src/ExampleTelemetryServerAdapter', ++ './src/ExampleTelemetryInitializer', ++ './src/ExampleTelemetryModelProvider' ], function ( legacyRegistry, - ExampleTelemetryServerAdapter, - ExampleTelemetryInitializer, - ExampleTelemetryModelProvider ++ ExampleTelemetryServerAdapter, ++ ExampleTelemetryInitializer, ++ ExampleTelemetryModelProvider ) { legacyRegistry.register("tutorials/telemetry", { "name": "Example Telemetry Adapter", @@ -2755,7 +2764,7 @@ define([ "key": "example.spacecraft", "glyph": "o" }, - { ++ { + "name": "Subsystem", + "key": "example.subsystem", + "glyph": "o", @@ -2934,6 +2943,7 @@ identifier, the pending promise is resolved. This `history` method will be used by a `telemetryService` provider which we will implement: + ```diff /*global define*/ @@ -3019,6 +3029,7 @@ Finally, note that we also have a `subscribe` method, to satisfy the interface o `telemetryService`, but this `subscribe` method currently does nothing. This script uses an `ExampleTelemetrySeries` class, which looks like: + ```diff /*global define*/ @@ -3050,6 +3061,7 @@ This takes the array of telemetry values (as returned by the server) and wraps it with the interface expected by the platform (the methods shown.) Finally, we expose this `telemetryService` provider declaratively: + ```diff define([ 'legacyRegistry', @@ -3226,7 +3238,7 @@ define( __tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ Here, we have added `subscribe` and `unsubscribe` methods which issue the -corresponding requests to the server. Seperately, we introduce the ability to +corresponding requests to the server. Separately, we introduce the ability to listen for `data` messages as they come in: These will contain the data associated with these subscriptions. @@ -3316,7 +3328,6 @@ define( } ); ``` - __tutorials/telemetry/src/ExampleTelemetryProvider.js__ A quick summary of these changes: diff --git a/example/eventGenerator/bundle.js b/example/eventGenerator/bundle.js index 60b91bbb9c..157fa62542 100644 --- a/example/eventGenerator/bundle.js +++ b/example/eventGenerator/bundle.js @@ -49,7 +49,7 @@ define([ { "key": "eventGenerator", "name": "Event Message Generator", - "glyph": "\u0066", + "cssclass": "icon-folder-new", "description": "For development use. Creates sample event message data that mimics a live data stream.", "priority": 10, "features": "creation", diff --git a/example/eventGenerator/data/transcript.json b/example/eventGenerator/data/transcript.json index 0416220e71..ce78735330 100644 --- a/example/eventGenerator/data/transcript.json +++ b/example/eventGenerator/data/transcript.json @@ -10,7 +10,7 @@ "LMP: 47 degrees.", "CC: Eagle, looking great. You're GO.", "CC: Roger. 1202. We copy it.", - "O1: LMP 35 degrees. 35 degrees. 750. Coming aown to 23.fl", + "O1: LMP 35 degrees. 35 degrees. 750. Coming down to 23.fl", "LMP: 700 feet, 21 down, 33 degrees.", "LMP: 600 feet, down at 19.", "LMP: 540 feet, down at - 30. Down at 15.", diff --git a/example/export/bundle.js b/example/export/bundle.js index fca07b789d..5322d53a83 100644 --- a/example/export/bundle.js +++ b/example/export/bundle.js @@ -36,7 +36,7 @@ define([ "name": "Export Telemetry as CSV", "implementation": ExportTelemetryAsCSVAction, "category": "contextual", - "glyph": "\u0033", + "cssclass": "icon-download", "depends": [ "exportService" ] } ] diff --git a/example/forms/res/templates/exampleForm.html b/example/forms/res/templates/exampleForm.html index 7d6e34b7dc..a315d7c9c9 100644 --- a/example/forms/res/templates/exampleForm.html +++ b/example/forms/res/templates/exampleForm.html @@ -20,12 +20,14 @@ at runtime from the About dialog for additional information. -->
- - - - - + +
  • Dirty: {{aForm.$dirty}}
  • @@ -33,11 +35,8 @@
-
-
-    
-
+        
     
\ No newline at end of file diff --git a/example/forms/src/ExampleFormController.js b/example/forms/src/ExampleFormController.js index dea579f075..f3f8c95a4e 100644 --- a/example/forms/src/ExampleFormController.js +++ b/example/forms/src/ExampleFormController.js @@ -78,27 +78,26 @@ define( items: [ { control: "button", - glyph: "1", - description: "Button A", + csslass: "icon-save", click: function () { - window.alert("A"); + window.alert("Save"); } }, { control: "button", - glyph: "2", + csslass: "icon-x", description: "Button B", click: function () { - window.alert("B"); + window.alert("Cancel"); } }, { control: "button", - glyph: "3", + csslass: "icon-trash", description: "Button C", disabled: true, click: function () { - window.alert("C"); + window.alert("Delete"); } } ] diff --git a/example/generator/bundle.js b/example/generator/bundle.js index e4e719149a..0eadfad5e9 100644 --- a/example/generator/bundle.js +++ b/example/generator/bundle.js @@ -86,7 +86,7 @@ define([ { "key": "generator", "name": "Sine Wave Generator", - "glyph": "\u0054", + "cssclass": "icon-telemetry", "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", "priority": 10, "features": "creation", diff --git a/example/imagery/bundle.js b/example/imagery/bundle.js index d0304f3f98..6232229e40 100644 --- a/example/imagery/bundle.js +++ b/example/imagery/bundle.js @@ -49,7 +49,7 @@ define([ { "key": "imagery", "name": "Example Imagery", - "glyph": "\u00e3", + "cssclass": "icon-image", "features": "creation", "description": "For development use. Creates example imagery data that mimics a live imagery stream.", "priority": 10, diff --git a/example/mobile/res/sass/mobile-example.scss b/example/mobile/res/sass/mobile-example.scss index f9f004baa4..99c14d305b 100644 --- a/example/mobile/res/sass/mobile-example.scss +++ b/example/mobile/res/sass/mobile-example.scss @@ -25,7 +25,7 @@ @include phoneandtablet { // Show the Create button - .create-btn-holder { + .create-button-holder { display: block !important; } } diff --git a/example/msl/bundle.js b/example/msl/bundle.js index 15c8191574..aefc3ffc16 100644 --- a/example/msl/bundle.js +++ b/example/msl/bundle.js @@ -43,18 +43,18 @@ define([ { "name":"Mars Science Laboratory", "key": "msl.curiosity", - "glyph": "o" + "cssclass": "icon-object" }, { "name": "Instrument", "key": "msl.instrument", - "glyph": "o", + "cssclass": "icon-object", "model": {"composition": []} }, { "name": "Measurement", "key": "msl.measurement", - "glyph": "\u0054", + "cssclass": "icon-telemetry", "model": {"telemetry": {}}, "telemetry": { "source": "rems.source", diff --git a/example/notifications/res/dialog-launch.html b/example/notifications/res/dialog-launch.html index 9eebd2e3e5..c810f7f605 100644 --- a/example/notifications/res/dialog-launch.html +++ b/example/notifications/res/dialog-launch.html @@ -1,9 +1,9 @@ - + - + Known | Unknown | Error | Info - Dialogs + \ No newline at end of file diff --git a/example/notifications/res/notification-launch.html b/example/notifications/res/notification-launch.html index e5f5cbac6b..41aee23e63 100644 --- a/example/notifications/res/notification-launch.html +++ b/example/notifications/res/notification-launch.html @@ -1,9 +1,9 @@ - + - + Success | Error | Alert | Progress - Notifications + \ No newline at end of file diff --git a/example/notifications/src/DialogLaunchIndicator.js b/example/notifications/src/DialogLaunchIndicator.js index 4a9a67689e..3d30e84be5 100644 --- a/example/notifications/src/DialogLaunchIndicator.js +++ b/example/notifications/src/DialogLaunchIndicator.js @@ -32,17 +32,15 @@ define( * launched for demonstration and testing purposes. * @constructor */ + function DialogLaunchIndicator() { } DialogLaunchIndicator.template = 'dialogLaunchTemplate'; - DialogLaunchIndicator.prototype.getGlyph = function () { - return "i"; - }; DialogLaunchIndicator.prototype.getGlyphClass = function () { - return 'caution'; + return 'ok'; }; DialogLaunchIndicator.prototype.getText = function () { return "Launch test dialog"; diff --git a/example/notifications/src/NotificationLaunchIndicator.js b/example/notifications/src/NotificationLaunchIndicator.js index 8c48d05bee..b839fa81d6 100644 --- a/example/notifications/src/NotificationLaunchIndicator.js +++ b/example/notifications/src/NotificationLaunchIndicator.js @@ -26,17 +26,21 @@ define( function () { "use strict"; + /** + * A tool for manually invoking notifications. When included this + * indicator will allow for notifications of different types to be + * launched for demonstration and testing purposes. + * @constructor + */ + function NotificationLaunchIndicator() { } NotificationLaunchIndicator.template = 'notificationLaunchTemplate'; - NotificationLaunchIndicator.prototype.getGlyph = function () { - return "i"; - }; NotificationLaunchIndicator.prototype.getGlyphClass = function () { - return 'caution'; + return 'ok'; }; NotificationLaunchIndicator.prototype.getText = function () { return "Launch notification"; diff --git a/example/plotOptions/bundle.js b/example/plotOptions/bundle.js index 25a78584b1..ae47d2a97c 100644 --- a/example/plotOptions/bundle.js +++ b/example/plotOptions/bundle.js @@ -81,7 +81,7 @@ define([ { "key": "plot", "name": "Example Telemetry Plot", - "glyph": "\u0074", + "cssclass": "icon-telemetry-panel", "description": "For development use. A plot for displaying telemetry.", "priority": 10, "delegates": [ @@ -129,7 +129,7 @@ define([ { "name": "Period", "control": "textfield", - "cssclass": "l-small l-numeric", + "cssclass": "l-input-sm l-numeric", "key": "period", "required": true, "property": [ diff --git a/example/profiling/src/DigestIndicator.js b/example/profiling/src/DigestIndicator.js index 1de2d0e973..826dd2f23f 100644 --- a/example/profiling/src/DigestIndicator.js +++ b/example/profiling/src/DigestIndicator.js @@ -59,11 +59,14 @@ define( update(); return { - getGlyph: function () { - return "."; - }, - getGlyphClass: function () { - return undefined; + /** + * Get the CSS class that defines the icon + * to display in this indicator. This will appear + * as a dataflow icon. + * @returns {string} the cssclass of the dataflow icon + */ + getCssClass: function () { + return "icon-connectivity"; }, getText: function () { return displayed + " digests/sec"; diff --git a/example/profiling/src/WatchIndicator.js b/example/profiling/src/WatchIndicator.js index f999de7e01..1e9ef5e386 100644 --- a/example/profiling/src/WatchIndicator.js +++ b/example/profiling/src/WatchIndicator.js @@ -55,24 +55,13 @@ define( return { /** - * Get the glyph (single character used as an icon) + * Get the CSS class (single character used as an icon) * to display in this indicator. This will return ".", - * which should appear as a dataflow icon. + * which should appear as a database icon. * @returns {string} the character of the database icon */ - getGlyph: function () { - return "E"; - }, - /** - * Get the name of the CSS class to apply to the glyph. - * This is used to color the glyph to match its - * state (one of ok, caution or err) - * @returns {string} the CSS class to apply to this glyph - */ - getGlyphClass: function () { - return (watches > 2000) ? "caution" : - (watches < 1000) ? "ok" : - undefined; + getCssClass: function () { + return "icon-database"; }, /** * Get the text that should appear in the indicator. diff --git a/example/worker/src/FibonacciIndicator.js b/example/worker/src/FibonacciIndicator.js index d2f25c8aed..777fb3d2f5 100644 --- a/example/worker/src/FibonacciIndicator.js +++ b/example/worker/src/FibonacciIndicator.js @@ -50,15 +50,12 @@ define( requestNext(); return { - getGlyph: function () { - return "?"; + getCssClass: function () { + return "icon-object-unknown"; }, getText: function () { return latest; }, - getGlyphClass: function () { - return ""; - }, getDescription: function () { return ""; } diff --git a/gulpfile.js b/gulpfile.js index fab0f056e0..ff994c6a2c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -42,6 +42,7 @@ var gulp = require('gulp'), main: 'main.js', dist: 'dist', assets: 'dist/assets', + reports: 'dist/reports', scss: ['./platform/**/*.scss', './example/**/*.scss'], scripts: [ 'main.js', 'platform/**/*.js', 'src/**/*.js' ], specs: [ 'platform/**/*Spec.js', 'src/**/*Spec.js' ], @@ -112,6 +113,10 @@ gulp.task('lint', function () { .pipe(jshint({ jasmine: true })); return merge(scriptLint, specLint) + .pipe(jshint.reporter('gulp-jshint-html-reporter', { + filename: paths.reports + '/lint/jshint-report.html', + createMissingFolders : true + })) .pipe(jshint.reporter('default')) .pipe(jshint.reporter('fail')); }); diff --git a/index.html b/index.html index e47efe91b5..fcd37cfb0b 100644 --- a/index.html +++ b/index.html @@ -19,16 +19,15 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> - - - - + + + + - - - - - - + + + +
diff --git a/karma.conf.js b/karma.conf.js index 17889dfe43..e682ec6d86 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -81,7 +81,7 @@ module.exports = function(config) { coverageReporter: { dir: process.env.CIRCLE_ARTIFACTS ? process.env.CIRCLE_ARTIFACTS + '/coverage' : - "dist/coverage", + "dist/reports/coverage", check: { global: { lines: 80 @@ -91,13 +91,13 @@ module.exports = function(config) { // HTML test reporting. htmlReporter: { - outputDir: "target/tests", + outputDir: "dist/reports/tests", preserveDescribeNesting: true, foldAll: false }, junitReporter: { - outputDir: process.env.CIRCLE_TEST_REPORTS || 'target/junit' + outputDir: process.env.CIRCLE_TEST_REPORTS || 'dist/reports/junit' }, // Continuous Integration mode. diff --git a/main.js b/main.js index 268c70060d..6968d8ff26 100644 --- a/main.js +++ b/main.js @@ -27,7 +27,8 @@ requirejs.config({ "angular": "bower_components/angular/angular.min", "angular-route": "bower_components/angular-route/angular-route.min", "csv": "bower_components/comma-separated-values/csv.min", - "es6-promise": "bower_components/es6-promise/promise.min", + "es6-promise": "bower_components/es6-promise/es6-promise.min", + "html2canvas": "bower_components/html2canvas/build/html2canvas.min", "moment": "bower_components/moment/moment", "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format", "saveAs": "bower_components/FileSaver.js/FileSaver.min", @@ -43,6 +44,9 @@ requirejs.config({ "angular-route": { "deps": ["angular"] }, + "html2canvas": { + "exports": "html2canvas" + }, "moment-duration-format": { "deps": ["moment"] }, diff --git a/package.json b/package.json index 3a58a4af19..e47a64e50e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openmct", - "version": "0.11.1-SNAPSHOT", + "version": "0.12.0-SNAPSHOT", "description": "The Open MCT core platform", "dependencies": { "express": "^4.13.1", @@ -14,6 +14,7 @@ "gulp": "^3.9.0", "gulp-jscs": "^3.0.2", "gulp-jshint": "^2.0.0", + "gulp-jshint-html-reporter": "^0.1.3", "gulp-rename": "^1.2.2", "gulp-replace-task": "^0.11.0", "gulp-requirejs-optimize": "^0.3.1", @@ -38,7 +39,7 @@ "mkdirp": "^0.5.1", "moment": "^2.11.1", "node-bourbon": "^4.2.3", - "phantomjs-prebuilt": "^2.1.0", + "phantomjs-prebuilt": "2.1.11 || >2.1.12 <3.0.0", "requirejs": "2.1.x", "split": "^1.0.0" }, diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js index e0a5758fff..5ff13d789e 100644 --- a/platform/commonUI/browse/bundle.js +++ b/platform/commonUI/browse/bundle.js @@ -231,7 +231,7 @@ define([ "$window" ], "group": "windowing", - "glyph": "y", + "cssclass": "icon-new-window", "priority": "preferred" }, { @@ -239,7 +239,6 @@ define([ "implementation": FullscreenAction, "category": "view-control", "group": "windowing", - "glyph": "z", "priority": "default" } ], @@ -247,7 +246,7 @@ define([ { "key": "items", "name": "Items", - "glyph": "9", + "cssclass": "icon-thumbs-strip", "description": "Grid of available items", "template": itemsTemplate, "uses": [ diff --git a/platform/commonUI/browse/res/templates/back-arrow.html b/platform/commonUI/browse/res/templates/back-arrow.html index 4a77e62d0b..44ac390f39 100644 --- a/platform/commonUI/browse/res/templates/back-arrow.html +++ b/platform/commonUI/browse/res/templates/back-arrow.html @@ -20,11 +20,8 @@ at runtime from the About dialog for additional information. --> - - - { diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 63197ba84f..aa1415ca81 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -31,7 +31,7 @@
+ class="holder flex-elem create-button-holder"> +
@@ -75,10 +79,6 @@ mct-object="navigatedObject" ng-model="treeModel"> -
diff --git a/platform/commonUI/browse/res/templates/browse/object-header.html b/platform/commonUI/browse/res/templates/browse/object-header.html index 7b80323fab..48a64796da 100644 --- a/platform/commonUI/browse/res/templates/browse/object-header.html +++ b/platform/commonUI/browse/res/templates/browse/object-header.html @@ -19,7 +19,7 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> -{{type.getGlyph()}} + {{parameters.mode}} {{model.name}} diff --git a/platform/commonUI/browse/res/templates/items/grid-item.html b/platform/commonUI/browse/res/templates/items/grid-item.html index 069ecf4a4a..153175eb00 100644 --- a/platform/commonUI/browse/res/templates/items/grid-item.html +++ b/platform/commonUI/browse/res/templates/items/grid-item.html @@ -23,14 +23,14 @@
-
O
+
- {{type.getGlyph()}} + -
}
+
{{model.name}}
diff --git a/platform/commonUI/browse/res/templates/menu-arrow.html b/platform/commonUI/browse/res/templates/menu-arrow.html index e3c1b935e7..83327760b0 100644 --- a/platform/commonUI/browse/res/templates/menu-arrow.html +++ b/platform/commonUI/browse/res/templates/menu-arrow.html @@ -21,6 +21,6 @@ --> - v + \ No newline at end of file diff --git a/platform/commonUI/browse/src/navigation/NavigateAction.js b/platform/commonUI/browse/src/navigation/NavigateAction.js index b3d935812a..6121213a51 100644 --- a/platform/commonUI/browse/src/navigation/NavigateAction.js +++ b/platform/commonUI/browse/src/navigation/NavigateAction.js @@ -48,20 +48,35 @@ define( */ NavigateAction.prototype.perform = function () { var self = this, - navigationAllowed = true; + navigateTo = this.domainObject, + currentObject = self.navigationService.getNavigation(); function allow() { - self.policyService.allow("navigation", self.navigationService.getNavigation(), self.domainObject, function (message) { + var navigationAllowed = true; + self.policyService.allow("navigation", currentObject, navigateTo, function (message) { navigationAllowed = self.$window.confirm(message + "\r\n\r\n" + " Are you sure you want to continue?"); }); return navigationAllowed; } - // Set navigation, and wrap like a promise - return this.$q.when( - allow() && this.navigationService.setNavigation(this.domainObject) - ); + function cancelIfEditing() { + var editing = currentObject.hasCapability('editor') && + currentObject.getCapability('editor').isEditContextRoot(); + + return self.$q.when(editing && currentObject.getCapability("editor").finish()); + } + + function navigate() { + return self.navigationService.setNavigation(navigateTo); + } + + if (allow()) { + return cancelIfEditing().then(navigate); + } else { + return this.$q.when(false); + } + }; /** diff --git a/platform/commonUI/browse/src/windowing/FullscreenAction.js b/platform/commonUI/browse/src/windowing/FullscreenAction.js index 2882f17079..975562af0c 100644 --- a/platform/commonUI/browse/src/windowing/FullscreenAction.js +++ b/platform/commonUI/browse/src/windowing/FullscreenAction.js @@ -46,12 +46,12 @@ define( }; FullscreenAction.prototype.getMetadata = function () { - // We override getMetadata, because the glyph and + // We override getMetadata, because the icon cssclass and // description need to be determined at run-time // based on whether or not we are currently // full screen. var metadata = Object.create(FullscreenAction); - metadata.glyph = screenfull.isFullscreen ? "_" : "z"; + metadata.cssclass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse"; metadata.description = screenfull.isFullscreen ? EXIT_FULLSCREEN : ENTER_FULLSCREEN; metadata.group = "windowing"; diff --git a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js index 003012372b..f9b756eeaa 100644 --- a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js +++ b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js @@ -32,7 +32,9 @@ define( mockQ, mockDomainObject, mockPolicyService, + mockNavigatedObject, mockWindow, + capabilities, action; function mockPromise(value) { @@ -44,6 +46,29 @@ define( } beforeEach(function () { + capabilities = {}; + + mockQ = { when: mockPromise }; + mockNavigatedObject = jasmine.createSpyObj( + "domainObject", + [ + "getId", + "getModel", + "hasCapability", + "getCapability" + ] + ); + + capabilities.editor = jasmine.createSpyObj("editorCapability", [ + "isEditContextRoot", + "finish" + ]); + + mockNavigatedObject.getCapability.andCallFake(function (capability) { + return capabilities[capability]; + }); + mockNavigatedObject.hasCapability.andReturn(false); + mockNavigationService = jasmine.createSpyObj( "navigationService", [ @@ -51,11 +76,14 @@ define( "getNavigation" ] ); - mockNavigationService.getNavigation.andReturn({}); - mockQ = { when: mockPromise }; + mockNavigationService.getNavigation.andReturn(mockNavigatedObject); + mockDomainObject = jasmine.createSpyObj( "domainObject", - ["getId", "getModel", "getCapability"] + [ + "getId", + "getModel" + ] ); mockPolicyService = jasmine.createSpyObj("policyService", @@ -112,6 +140,21 @@ define( }); }); + describe("in edit mode", function () { + beforeEach(function () { + mockNavigatedObject.hasCapability.andCallFake(function (capability) { + return capability === "editor"; + }); + capabilities.editor.isEditContextRoot.andReturn(true); + }); + + it("finishes editing if in edit mode", function () { + action.perform(); + expect(capabilities.editor.finish) + .toHaveBeenCalled(); + }); + }); + it("is only applicable when a domain object is in context", function () { expect(NavigateAction.appliesTo({})).toBeFalsy(); expect(NavigateAction.appliesTo({ diff --git a/platform/commonUI/browse/test/windowing/FullscreenActionSpec.js b/platform/commonUI/browse/test/windowing/FullscreenActionSpec.js index bf2d520b3a..913535fa59 100644 --- a/platform/commonUI/browse/test/windowing/FullscreenActionSpec.js +++ b/platform/commonUI/browse/test/windowing/FullscreenActionSpec.js @@ -51,7 +51,7 @@ define( }); it("provides displayable metadata", function () { - expect(action.getMetadata().glyph).toBeDefined(); + expect(action.getMetadata().cssclass).toBeDefined(); }); }); diff --git a/platform/commonUI/dialog/res/templates/dialog.html b/platform/commonUI/dialog/res/templates/dialog.html index 9df10e0cd0..85a496ce57 100644 --- a/platform/commonUI/dialog/res/templates/dialog.html +++ b/platform/commonUI/dialog/res/templates/dialog.html @@ -21,21 +21,22 @@ -->
{{ngModel.title}}
-
All fields marked * are required.
+
All fields marked are required.
- OK - Cancel diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 8568fe5fb1..6a2be98d99 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -16,11 +16,11 @@
{{dialogOption.label}} - {{ngModel.primaryOption.label}} diff --git a/platform/commonUI/dialog/res/templates/overlay-message-list.html b/platform/commonUI/dialog/res/templates/overlay-message-list.html index 5a7a48192f..299d19639c 100644 --- a/platform/commonUI/dialog/res/templates/overlay-message-list.html +++ b/platform/commonUI/dialog/res/templates/overlay-message-list.html @@ -2,7 +2,8 @@
{{ngModel.dialog.title}}
-
Displaying {{ngModel.dialog.messages.length}} messages +
Displaying {{ngModel.dialog.messages.length}} messages
@@ -12,7 +13,7 @@
{{dialogAction.label}} diff --git a/platform/commonUI/dialog/res/templates/overlay-options.html b/platform/commonUI/dialog/res/templates/overlay-options.html index 4d73c59613..89b8be8242 100644 --- a/platform/commonUI/dialog/res/templates/overlay-options.html +++ b/platform/commonUI/dialog/res/templates/overlay-options.html @@ -33,7 +33,7 @@
diff --git a/platform/commonUI/dialog/res/templates/overlay.html b/platform/commonUI/dialog/res/templates/overlay.html index 3908952db9..215fe97aac 100644 --- a/platform/commonUI/dialog/res/templates/overlay.html +++ b/platform/commonUI/dialog/res/templates/overlay.html @@ -19,12 +19,12 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> -
+
x + class="close icon-x">
\ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index ca0a4b09b7..61e3673dd6 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -187,7 +187,7 @@ define( /** * A description of the model options that may be passed to the - * showBlockingMessage method. Note that the DialogModel desribed + * showBlockingMessage method. Note that the DialogModel described * here is shared with the Notifications framework. * @see NotificationService * @@ -200,6 +200,9 @@ define( * shown above a progress bar to indicate what's happening. * @property {number} progress a percentage value (1-100) * indicating the completion of the blocking task + * @property {boolean} delay adds a brief delay before loading + * the dialog. Useful for removing the dialog flicker when the + * conditions for displaying the dialog change rapidly. * @property {string} progressText the message to show below a * progress bar to indicate progress. For example, this might be * used to indicate time remaining, or items still to process. diff --git a/platform/commonUI/edit/bundle.js b/platform/commonUI/edit/bundle.js index 20d42be719..d2e9a684ae 100644 --- a/platform/commonUI/edit/bundle.js +++ b/platform/commonUI/edit/bundle.js @@ -31,6 +31,7 @@ define([ "./src/actions/PropertiesAction", "./src/actions/RemoveAction", "./src/actions/SaveAction", + "./src/actions/SaveAndStopEditingAction", "./src/actions/SaveAsAction", "./src/actions/CancelAction", "./src/policies/EditActionPolicy", @@ -70,6 +71,7 @@ define([ PropertiesAction, RemoveAction, SaveAction, + SaveAndStopEditingAction, SaveAsAction, CancelAction, EditActionPolicy, @@ -174,7 +176,7 @@ define([ ], "description": "Edit", "category": "view-control", - "glyph": "p" + "cssclass": "major icon-pencil" }, { "key": "properties", @@ -183,7 +185,7 @@ define([ "view-control" ], "implementation": PropertiesAction, - "glyph": "p", + "cssclass": "major icon-pencil", "name": "Edit Properties...", "description": "Edit properties of this object.", "depends": [ @@ -194,7 +196,7 @@ define([ "key": "remove", "category": "contextual", "implementation": RemoveAction, - "glyph": "Z", + "cssclass": "icon-trash", "name": "Remove", "description": "Remove this object from its containing object.", "depends": [ @@ -202,27 +204,38 @@ define([ ] }, { - "key": "save", - "category": "conclude-editing", - "implementation": SaveAction, - "name": "Save", + "key": "save-and-stop-editing", + "category": "save", + "implementation": SaveAndStopEditingAction, + "name": "Save and Finish Editing", + "cssclass": "icon-save labeled", "description": "Save changes made to these objects.", "depends": [ "dialogService" - ], - "priority": "mandatory" + ] }, { "key": "save", - "category": "conclude-editing", + "category": "save", + "implementation": SaveAction, + "name": "Save and Continue Editing", + "cssclass": "icon-save labeled", + "description": "Save changes made to these objects.", + "depends": [ + "dialogService" + ] + }, + { + "key": "save-as", + "category": "save", "implementation": SaveAsAction, - "name": "Save", + "name": "Save As...", + "cssclass": "icon-save labeled", "description": "Save changes made to these objects.", "depends": [ "$injector", "policyService", "dialogService", - "creationService", "copyService" ], "priority": "mandatory" @@ -231,7 +244,10 @@ define([ "key": "cancel", "category": "conclude-editing", "implementation": CancelAction, - "name": "Cancel", + // Because we use the name as label for edit buttons and mct-control buttons need + // the label to be set to undefined in order to not apply the labeled CSS rule. + "name": undefined, + "cssclass": "icon-x no-label", "description": "Discard changes made to these objects.", "depends": [] } @@ -383,7 +399,7 @@ define([ "constants": [ { "key": "editModeBlacklist", - "value": ["copy", "follow", "window", "link", "locate"] + "value": ["copy", "follow", "link", "locate"] }, { "key": "nonEditContextBlacklist", diff --git a/platform/commonUI/edit/res/templates/create/create-button.html b/platform/commonUI/edit/res/templates/create/create-button.html index 77c6198cae..8a1e799783 100644 --- a/platform/commonUI/edit/res/templates/create/create-button.html +++ b/platform/commonUI/edit/res/templates/create/create-button.html @@ -20,7 +20,7 @@ at runtime from the About dialog for additional information. --> -
+
Create