Compare commits
	
		
			185 Commits
		
	
	
		
			open208
			...
			open-rjs-o
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					5bc30dbf2a | ||
| 
						 | 
					71bd77caf4 | ||
| 
						 | 
					f275177aa6 | ||
| 
						 | 
					05908aa877 | ||
| 
						 | 
					9b5c62d2d5 | ||
| 
						 | 
					297e6b00e1 | ||
| 
						 | 
					848af50d4e | ||
| 
						 | 
					97e93c3104 | ||
| 
						 | 
					731cdb343a | ||
| 
						 | 
					d9664d4574 | ||
| 
						 | 
					5a0e949b62 | ||
| 
						 | 
					28a6eabfb6 | ||
| 
						 | 
					6e43342157 | ||
| 
						 | 
					6ce4c013d7 | ||
| 
						 | 
					1272fb8509 | ||
| 
						 | 
					e84c88b8c0 | ||
| 
						 | 
					3a8911e542 | ||
| 
						 | 
					2a57b83cb0 | ||
| 
						 | 
					29ecb68b9b | ||
| 
						 | 
					5b0af8773b | ||
| 
						 | 
					89a93e2966 | ||
| 
						 | 
					038322e9aa | ||
| 
						 | 
					6cef663db5 | ||
| 
						 | 
					b2f5861458 | ||
| 
						 | 
					f8809ce67f | ||
| 
						 | 
					3c97eb6014 | ||
| 
						 | 
					3498b0a50a | ||
| 
						 | 
					ed7e0d8b0a | ||
| 
						 | 
					f3828ba516 | ||
| 
						 | 
					2a48c25453 | ||
| 
						 | 
					096da0cb94 | ||
| 
						 | 
					ca8a07e1ae | ||
| 
						 | 
					c02f965460 | ||
| 
						 | 
					5f440bb6de | ||
| 
						 | 
					fc729279ec | ||
| 
						 | 
					dd0f9ab74e | ||
| 
						 | 
					d2a4a85e04 | ||
| 
						 | 
					37890280ae | ||
| 
						 | 
					a6ceae4045 | ||
| 
						 | 
					3447e735dc | ||
| 
						 | 
					989c937ce1 | ||
| 
						 | 
					e0608ddee0 | ||
| 
						 | 
					b35224061f | ||
| 
						 | 
					b25576aed8 | ||
| 
						 | 
					d5f054e328 | ||
| 
						 | 
					eb4959cf49 | ||
| 
						 | 
					cce415fc51 | ||
| 
						 | 
					00f96c314a | ||
| 
						 | 
					fefd27162c | ||
| 
						 | 
					b388c76e45 | ||
| 
						 | 
					aaeaf3a096 | ||
| 
						 | 
					57efe4e0d1 | ||
| 
						 | 
					dd4dbc9326 | ||
| 
						 | 
					6aa77ff468 | ||
| 
						 | 
					f8099550bd | ||
| 
						 | 
					96249e6bcc | ||
| 
						 | 
					6e391098a3 | ||
| 
						 | 
					baec0f9719 | ||
| 
						 | 
					6aab9f4e34 | ||
| 
						 | 
					1bf73935e4 | ||
| 
						 | 
					49e51d0a62 | ||
| 
						 | 
					3e7bc2f37f | ||
| 
						 | 
					0f56fd2561 | ||
| 
						 | 
					9f0114eb39 | ||
| 
						 | 
					4b82893c36 | ||
| 
						 | 
					734e979c94 | ||
| 
						 | 
					983973843e | ||
| 
						 | 
					3b427c31a2 | ||
| 
						 | 
					cee0ecf0ef | ||
| 
						 | 
					8e3c5db3bf | ||
| 
						 | 
					11d8daf3ed | ||
| 
						 | 
					9953e16415 | ||
| 
						 | 
					fe600de0f7 | ||
| 
						 | 
					386f1f20ff | ||
| 
						 | 
					5e07951892 | ||
| 
						 | 
					2514e44083 | ||
| 
						 | 
					20d9c7158e | ||
| 
						 | 
					d8e319ebf8 | ||
| 
						 | 
					a39cbbd917 | ||
| 
						 | 
					8df27a1c05 | ||
| 
						 | 
					26cf9c14f4 | ||
| 
						 | 
					03edd26e17 | ||
| 
						 | 
					571f6d183a | ||
| 
						 | 
					1292e39c46 | ||
| 
						 | 
					6fe3f82fb1 | ||
| 
						 | 
					da8fb99e82 | ||
| 
						 | 
					c8d77bc2db | ||
| 
						 | 
					b5e52fce75 | ||
| 
						 | 
					3afcb52934 | ||
| 
						 | 
					677b0cffec | ||
| 
						 | 
					248bc68f0d | ||
| 
						 | 
					02050fa3ef | ||
| 
						 | 
					57d23c3696 | ||
| 
						 | 
					8babfc5ca9 | ||
| 
						 | 
					271b5d1a73 | ||
| 
						 | 
					7d4e7a0925 | ||
| 
						 | 
					92f5d5f190 | ||
| 
						 | 
					1731b985fc | ||
| 
						 | 
					bd4590ad9d | ||
| 
						 | 
					55fc60ec82 | ||
| 
						 | 
					ab075e9ad8 | ||
| 
						 | 
					3ac1710d83 | ||
| 
						 | 
					730878938e | ||
| 
						 | 
					3fd4304de1 | ||
| 
						 | 
					db7224486c | ||
| 
						 | 
					424953c894 | ||
| 
						 | 
					6d0f3c7faa | ||
| 
						 | 
					434a52ded3 | ||
| 
						 | 
					2ec906e2d4 | ||
| 
						 | 
					ffff13205a | ||
| 
						 | 
					16efd85dfc | ||
| 
						 | 
					1ef09ffbdd | ||
| 
						 | 
					976ecce075 | ||
| 
						 | 
					87a51a9eb3 | ||
| 
						 | 
					c84de00e80 | ||
| 
						 | 
					91997ced01 | ||
| 
						 | 
					7a4be9e67e | ||
| 
						 | 
					eb942b0bf7 | ||
| 
						 | 
					1cf23c7ad6 | ||
| 
						 | 
					15ec9df538 | ||
| 
						 | 
					d6e2895666 | ||
| 
						 | 
					7974ffdda2 | ||
| 
						 | 
					d5858622ba | ||
| 
						 | 
					9656e09066 | ||
| 
						 | 
					096fee8b6d | ||
| 
						 | 
					0635e7c38e | ||
| 
						 | 
					845b1dcd6f | ||
| 
						 | 
					2e959e8503 | ||
| 
						 | 
					e6c9cbf0cd | ||
| 
						 | 
					fb0ba0cff9 | ||
| 
						 | 
					573e5608fc | ||
| 
						 | 
					37a7c2b1df | ||
| 
						 | 
					38274728f6 | ||
| 
						 | 
					49b3d67272 | ||
| 
						 | 
					400b992ec3 | ||
| 
						 | 
					32815d8427 | ||
| 
						 | 
					3e25d17702 | ||
| 
						 | 
					b5d1118a3f | ||
| 
						 | 
					5b9e43f8ff | ||
| 
						 | 
					3ffa6f70aa | ||
| 
						 | 
					0d07c3c289 | ||
| 
						 | 
					29fdb6d641 | ||
| 
						 | 
					39d007470a | ||
| 
						 | 
					285c8cbd1e | ||
| 
						 | 
					ef5a26dfcc | ||
| 
						 | 
					8363302caf | ||
| 
						 | 
					04ce2f985a | ||
| 
						 | 
					a14f30c03c | ||
| 
						 | 
					bd85392b54 | ||
| 
						 | 
					7c427e0b6e | ||
| 
						 | 
					6cf8335f31 | ||
| 
						 | 
					647a1403d0 | ||
| 
						 | 
					1d9b8f34e2 | ||
| 
						 | 
					adf119007b | ||
| 
						 | 
					7114e9b150 | ||
| 
						 | 
					76c1f5bfe9 | ||
| 
						 | 
					33f88d30ed | ||
| 
						 | 
					ffdcbece56 | ||
| 
						 | 
					44eb723efb | ||
| 
						 | 
					689f80bb23 | ||
| 
						 | 
					a2db98d275 | ||
| 
						 | 
					49b983cd3a | ||
| 
						 | 
					ad60b9225e | ||
| 
						 | 
					0c096db8bd | ||
| 
						 | 
					87684e0945 | ||
| 
						 | 
					1d13b245f9 | ||
| 
						 | 
					e31d9decdc | ||
| 
						 | 
					06436bb876 | ||
| 
						 | 
					d8f3f0f430 | ||
| 
						 | 
					177c1874b9 | ||
| 
						 | 
					eef801f1ae | ||
| 
						 | 
					d69cf6c6fe | ||
| 
						 | 
					0a9c162f26 | ||
| 
						 | 
					942fa46022 | ||
| 
						 | 
					74aff1b407 | ||
| 
						 | 
					dbff9e2125 | ||
| 
						 | 
					bed1556a3a | ||
| 
						 | 
					822b4ae96f | ||
| 
						 | 
					99f3b986b6 | ||
| 
						 | 
					fd4c1ea747 | ||
| 
						 | 
					944a5a7424 | ||
| 
						 | 
					dda2c89a58 | ||
| 
						 | 
					c8cfbf5281 | ||
| 
						 | 
					9f383ab101 | ||
| 
						 | 
					7811d50372 | 
@@ -291,7 +291,7 @@ checklist.)
 | 
			
		||||
1. Changes address original issue?
 | 
			
		||||
2. Unit tests included and/or updated with changes?
 | 
			
		||||
3. Command line build passes?
 | 
			
		||||
4. Expect to pass code review?
 | 
			
		||||
4. Changes have been smoke-tested?
 | 
			
		||||
 | 
			
		||||
### Reviewer Checklist
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								build.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								build.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 require*/
 | 
			
		||||
var requirejs = require("requirejs"),
 | 
			
		||||
    globby = require("globby"),
 | 
			
		||||
    fs = require("fs"),
 | 
			
		||||
    bundles = fs.readFileSync("bundles.json", 'utf8'),
 | 
			
		||||
    scripts = [],
 | 
			
		||||
    templates = [],
 | 
			
		||||
    contents,
 | 
			
		||||
    index = fs.readFileSync("index.html", 'utf8');
 | 
			
		||||
 | 
			
		||||
function trimJsExtension(filename) {
 | 
			
		||||
    return filename.replace(/\.js$/, "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JSON.parse(bundles).forEach(function (bundle) {
 | 
			
		||||
    scripts = scripts.concat(globby.sync(bundle + "/src/**/*.js"));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
requirejs.optimize({
 | 
			
		||||
    baseUrl: ".",
 | 
			
		||||
    name: "main",
 | 
			
		||||
    out: "target/main.js",
 | 
			
		||||
    paths: {
 | 
			
		||||
        'es6-promise': 'platform/framework/lib/es6-promise-2.0.0.min',
 | 
			
		||||
        'moment': 'platform/telemetry/lib/moment.min',
 | 
			
		||||
        'moment-duration-format': 'platform/features/clock/lib/moment-duration-format',
 | 
			
		||||
        'uuid': 'platform/core/lib/uuid'
 | 
			
		||||
    },
 | 
			
		||||
    shim: {
 | 
			
		||||
        'moment-duration-format': {
 | 
			
		||||
            deps: [ 'moment' ]
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    include: scripts.map(trimJsExtension),
 | 
			
		||||
    exclude: globby.sync("platform/framework/lib/**/*.js").map(trimJsExtension)
 | 
			
		||||
});
 | 
			
		||||
@@ -25,11 +25,13 @@
 | 
			
		||||
    "platform/features/timeline",
 | 
			
		||||
    "platform/forms",
 | 
			
		||||
    "platform/identity",
 | 
			
		||||
    "platform/persistence/aggregator",
 | 
			
		||||
    "platform/persistence/local",
 | 
			
		||||
    "platform/persistence/queue",
 | 
			
		||||
    "platform/policy",
 | 
			
		||||
    "platform/entanglement",
 | 
			
		||||
    "platform/search",
 | 
			
		||||
    "platform/status",
 | 
			
		||||
 | 
			
		||||
    "example/imagery",
 | 
			
		||||
    "example/eventGenerator",
 | 
			
		||||
 
 | 
			
		||||
@@ -677,6 +677,40 @@ If the provided capability has no invoke method, the return value here functions
 | 
			
		||||
as `getCapability` including returning `undefined` if the capability is not 
 | 
			
		||||
exposed.
 | 
			
		||||
 | 
			
		||||
### Identifier Syntax
 | 
			
		||||
 | 
			
		||||
For most purposes, a domain object identifier can be treated as a purely
 | 
			
		||||
symbolic string; these are typically generated by Open MCT Web and plug-ins
 | 
			
		||||
should rarely be concerned with its internal structure.
 | 
			
		||||
 | 
			
		||||
A domain object identifier has one or two parts, separated by a colon.
 | 
			
		||||
 | 
			
		||||
* If two parts are present, the part before the colon refers to the space
 | 
			
		||||
  in which the domain object resides. This may be a persistence space or
 | 
			
		||||
  a purely symbolic space recognized by a specific model provider. The
 | 
			
		||||
  part after the colon is the key to use when looking up the domain object
 | 
			
		||||
  model within that space.
 | 
			
		||||
* If only one part is present, the domain object has no space specified,
 | 
			
		||||
  and may presume to reside in the application-configured default space
 | 
			
		||||
  defined by the `PERSISTENCE_SPACE` constant.
 | 
			
		||||
* Both the key and the space identifier may consist of any combination
 | 
			
		||||
  of alphanumeric characters, underscores, dashes, and periods.
 | 
			
		||||
 | 
			
		||||
Some examples:
 | 
			
		||||
 | 
			
		||||
* A domain object with the identifier `foo:xyz` would have its model
 | 
			
		||||
  loaded using key `xyz` from persistence space `foo`.
 | 
			
		||||
* A domain object with the identifier `bar` would have its model loaded
 | 
			
		||||
  using key `bar` from the space identified by the `PERSISTENCE_SPACE`
 | 
			
		||||
  constant.
 | 
			
		||||
 | 
			
		||||
```bnf
 | 
			
		||||
<identifier> ::= <space> ":" <key> | <key>
 | 
			
		||||
<space> ::= <id char>+
 | 
			
		||||
<key> ::= <id char>+
 | 
			
		||||
<id char> ::= <letter> | <digit> | "-" | "." | "_"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Domain Object Actions
 | 
			
		||||
 | 
			
		||||
An `Action` is behavior that can be performed upon/using a `DomainObject`. An 
 | 
			
		||||
@@ -907,6 +941,12 @@ look at field  (see below) to determine which field in the model should be
 | 
			
		||||
modified. 
 | 
			
		||||
* `ngRequired`: True if input is required.
 | 
			
		||||
* `ngPattern`: The pattern to match against (for text entry)
 | 
			
		||||
* `ngBlur`: A function that may be invoked to evaluate the expression
 | 
			
		||||
  associated with the `ng-blur` attribute associated with the control.
 | 
			
		||||
  * This should be called when the control has lost focus; for controls
 | 
			
		||||
    which simply wrap or augment `input` elements, this should be fired
 | 
			
		||||
    on `blur` events associated with those elements, while more complex
 | 
			
		||||
    custom controls may fire this at the end of more specific interactions.
 | 
			
		||||
* `options`: The options for this control, as passed from the `options` property 
 | 
			
		||||
of an individual row definition. 
 | 
			
		||||
* `field`: Name of the field in `ngModel` which will hold the value for this 
 | 
			
		||||
@@ -1254,6 +1294,22 @@ object, or the current view proxy.
 | 
			
		||||
* `all()`: Get an array of all objects in the selection state. Will include 
 | 
			
		||||
either or both of the view proxy and selected object. 
 | 
			
		||||
 | 
			
		||||
## Workers Category
 | 
			
		||||
 | 
			
		||||
The `workers` extension category allows scripts to be run as web workers
 | 
			
		||||
using the `workerService`.
 | 
			
		||||
 | 
			
		||||
An extension of this category has no implementation. The following properties
 | 
			
		||||
are supported:
 | 
			
		||||
 | 
			
		||||
* `key`: A symbolic string used to identify this worker.
 | 
			
		||||
* `workerUrl`: The path, relative to this bundle's `src` folder, where
 | 
			
		||||
  this worker's source code resides.
 | 
			
		||||
* `shared`: Optional; a boolean flag which, if true, indicates that this
 | 
			
		||||
  worker should be instantiated as a
 | 
			
		||||
  [`SharedWorker`](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker).
 | 
			
		||||
  Default value is `false`.
 | 
			
		||||
 | 
			
		||||
# Directives
 | 
			
		||||
 | 
			
		||||
Open MCT Web defines several Angular directives that are intended for use both 
 | 
			
		||||
@@ -1849,6 +1905,14 @@ the TelemetrySeries itself, in that order.
 | 
			
		||||
* `getSeries(domainObject)`: Get the latest `TelemetrySeries` (as resulted from 
 | 
			
		||||
a previous `request(...)` call) available for this domain object.
 | 
			
		||||
 | 
			
		||||
### Worker Service
 | 
			
		||||
 | 
			
		||||
The `workerService` may be used to run web workers defined via the
 | 
			
		||||
`workers` extension category. It has the following method:
 | 
			
		||||
 | 
			
		||||
* `run(key)`: Run the worker identified by the provided `key`. Returns
 | 
			
		||||
  a `Worker` (or `SharedWorker`, if the specified worker is defined
 | 
			
		||||
  as a shared worker); if the `key` is unknown, returns `undefined`.
 | 
			
		||||
 | 
			
		||||
# Models
 | 
			
		||||
Domain object models in Open MCT Web are JavaScript objects describing the 
 | 
			
		||||
@@ -2056,6 +2120,31 @@ objects which has a `relationships` property in their model, whose value is an
 | 
			
		||||
object containing key-value pairs, where keys are strings identifying 
 | 
			
		||||
relationship types, and values are arrays of domain object identifiers.
 | 
			
		||||
 | 
			
		||||
## Status Capability
 | 
			
		||||
 | 
			
		||||
The `status` capability provides a way to flag domain objects as possessing
 | 
			
		||||
certain states, represented as simple strings. These states, in turn, are
 | 
			
		||||
reflected on `mct-representation` elements as classes (prefixed with
 | 
			
		||||
`s-status-`.) The `status` capability has the following interface:
 | 
			
		||||
 | 
			
		||||
* `get()`: Returns an array of all status strings that currently apply
 | 
			
		||||
  to this object.
 | 
			
		||||
* `set(status, state)`: Adds or removes a status flag to this domain object.
 | 
			
		||||
  The `status` argument is the string to set; `state` is a boolean
 | 
			
		||||
  indicating whether this status should be included (true) or removed (false).
 | 
			
		||||
* `listen(callback)`: Listen for changes in status. The provided `callback`
 | 
			
		||||
  will be invoked with an array of all current status strings whenever status
 | 
			
		||||
  changes.
 | 
			
		||||
 | 
			
		||||
Plug-ins may add and/or recognize arbitrary status flags. Flags defined
 | 
			
		||||
and/or supported by the platform are:
 | 
			
		||||
 | 
			
		||||
 Status    | CSS Class          | Meaning
 | 
			
		||||
-----------|--------------------|-----------------------------------
 | 
			
		||||
`editing`  | `s-status-editing` | Domain object is being edited.
 | 
			
		||||
`pending`  | `s-status-pending` | Domain object is partially loaded.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Telemetry Capability
 | 
			
		||||
 | 
			
		||||
The telemetry capability provides a means for accessing telemetry data 
 | 
			
		||||
@@ -2156,7 +2245,7 @@ options. The sources can be deployed in the same directory structure used during
 | 
			
		||||
development. A few utilities are included to support development processes.
 | 
			
		||||
 | 
			
		||||
## Command-line Build
 | 
			
		||||
Open MCT Web includes a script for building via command line using Maven 3.0.4 
 | 
			
		||||
Open MCT Web includes a script for building via command line using Maven 3.3.9
 | 
			
		||||
https://maven.apache.org/ .
 | 
			
		||||
        
 | 
			
		||||
Invoking mvn clean install will:
 | 
			
		||||
@@ -2328,6 +2417,11 @@ default paths to reach external services are all correct.
 | 
			
		||||
 | 
			
		||||
### Configuration Constants
 | 
			
		||||
 | 
			
		||||
The following constants have global significance:
 | 
			
		||||
* `PERSISTENCE_SPACE`: The space in which domain objects should be persisted
 | 
			
		||||
  (or read from) when not otherwise specified. Typically this will not need
 | 
			
		||||
  to be overridden by other bundles, but persistence adapters may wish to
 | 
			
		||||
  consume this constant in order to provide persistence for that space.
 | 
			
		||||
 | 
			
		||||
The following configuration constants are recognized by Open MCT Web bundles:
 | 
			
		||||
* Common UI elements - `platform/commonUI/general`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										161
									
								
								docs/src/process/cycle.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								docs/src/process/cycle.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
			
		||||
# Development Cycle
 | 
			
		||||
 | 
			
		||||
Development of Open MCT Web occurs on an iterative cycle of
 | 
			
		||||
sprints and releases.
 | 
			
		||||
 | 
			
		||||
* A _sprint_ is three weeks in duration, and represents a
 | 
			
		||||
  set of improvements that can be completed and tested by the
 | 
			
		||||
  development team. Software at the end of the sprint is
 | 
			
		||||
  "semi-stable"; it will have undergone reduced testing and may carry
 | 
			
		||||
  defects or usability issues of lower severity, particularly if
 | 
			
		||||
  there are workarounds.
 | 
			
		||||
* A _release_ occurs every four sprints. Releases are stable, and
 | 
			
		||||
  will have undergone full acceptance testing to ensure that the
 | 
			
		||||
  software behaves correctly and usably.
 | 
			
		||||
 | 
			
		||||
## Roles
 | 
			
		||||
 | 
			
		||||
The sprint process assumes the presence of a __project manager.__
 | 
			
		||||
The project manager is responsible for
 | 
			
		||||
making tactical decisions about what development work will be
 | 
			
		||||
performed, and for coordinating with stakeholders to arrive at
 | 
			
		||||
higher-level strategic decisions about desired functionality
 | 
			
		||||
and characteristics of the software, major external milestones,
 | 
			
		||||
and so forth.
 | 
			
		||||
 | 
			
		||||
In the absence of a dedicated project manager, this role may be rotated
 | 
			
		||||
among members of the development team on a per-sprint basis.
 | 
			
		||||
 | 
			
		||||
Responsibilities of the project manager including:
 | 
			
		||||
 | 
			
		||||
* Maintaining (with agreement of stakeholders) a "road map" of work
 | 
			
		||||
  planned for future releases/sprints; this should be higher-level,
 | 
			
		||||
  usually expressed as "themes",
 | 
			
		||||
  with just enough specificity to gauge feasibility of plans,
 | 
			
		||||
  relate work back to milestones, and identify longer-term
 | 
			
		||||
  dependencies.
 | 
			
		||||
* Determining (with assistance from the rest of the team) which
 | 
			
		||||
  issues to work on in a given sprint and how they shall be
 | 
			
		||||
  assigned.
 | 
			
		||||
* Pre-planning subsequent sprints to ensure that all members of the
 | 
			
		||||
  team always have a clear direction.
 | 
			
		||||
* Scheduling and/or ensuring adherence to
 | 
			
		||||
  [process points](#process-points).
 | 
			
		||||
* Responding to changes within the sprint (shifting priorities,
 | 
			
		||||
  new issues) and re-allocating work for the sprint as needed.
 | 
			
		||||
 | 
			
		||||
## Sprint Calendar
 | 
			
		||||
 | 
			
		||||
Certain [process points](#process-points) are regularly scheduled in
 | 
			
		||||
the sprint cycle.
 | 
			
		||||
 | 
			
		||||
### Sprints by Release
 | 
			
		||||
 | 
			
		||||
Allocation of work among sprints should be planned relative to release
 | 
			
		||||
goals and milestones. As a general guideline, higher-risk work (large
 | 
			
		||||
new features which may carry new defects, major refactoring, design
 | 
			
		||||
changes with uncertain effects on usability) should be allocated to
 | 
			
		||||
earlier sprints, allowing for time in later sprints to ensure stability.
 | 
			
		||||
 | 
			
		||||
| Sprint | Focus                                                   |
 | 
			
		||||
|:------:|:--------------------------------------------------------|
 | 
			
		||||
| __1__  | Prototyping, design, experimentation.                   |
 | 
			
		||||
| __2__  | New features, refinements, enhancements.                |
 | 
			
		||||
| __3__  | Feature completion, low-risk enhancements, bug fixing.  |
 | 
			
		||||
| __4__  | Stability & quality assurance.                          |
 | 
			
		||||
 | 
			
		||||
### Sprints 1-3
 | 
			
		||||
 | 
			
		||||
The first three sprints of a release are primarily centered around
 | 
			
		||||
development work, with regular acceptance testing in the third
 | 
			
		||||
week. During this third week, the top priority should be passing
 | 
			
		||||
acceptance testing (e.g. by resolving any blockers found); any
 | 
			
		||||
resources not needed for this effort should be used to begin work
 | 
			
		||||
for the subsequent sprint.
 | 
			
		||||
 | 
			
		||||
| Week  | Mon                       | Tue    | Wed | Thu                          | Fri         |
 | 
			
		||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
 | 
			
		||||
| __1__ | Sprint plan               | Tag-up |     |                              |             |
 | 
			
		||||
| __2__ |                           | Tag-up |     |                              | Code freeze |
 | 
			
		||||
| __3__ | Per-sprint testing        | Triage |     | _Per-sprint testing*_        | Ship        |
 | 
			
		||||
 | 
			
		||||
* If necessary.
 | 
			
		||||
 | 
			
		||||
### Sprint 4
 | 
			
		||||
 | 
			
		||||
The software must be stable at the end of the fourth sprint; because of
 | 
			
		||||
this, the fourth sprint is scheduled differently, with a heightened
 | 
			
		||||
emphasis on testing.
 | 
			
		||||
 | 
			
		||||
| Week   | Mon                       | Tue    | Wed | Thu                          | Fri         |
 | 
			
		||||
|-------:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
 | 
			
		||||
| __1__  | Sprint plan               | Tag-up |     |                              | Code freeze |
 | 
			
		||||
| __2__  | Per-release testing       | Triage |     |                              |             |
 | 
			
		||||
| __3__  | _Per-release testing*_    | Triage |     | _Per-release testing*_       | Ship        |
 | 
			
		||||
 | 
			
		||||
* If necessary.
 | 
			
		||||
 | 
			
		||||
## Process Points
 | 
			
		||||
 | 
			
		||||
* __Sprint plan.__ Project manager allocates issues based on
 | 
			
		||||
  theme(s) for sprint, then reviews with team. Each team member
 | 
			
		||||
  should have roughly two weeks of work allocated (to allow time
 | 
			
		||||
  in the third week for testing of work completed.)
 | 
			
		||||
  * Project manager should also sketch out subsequent sprint so
 | 
			
		||||
    that team may begin work for that sprint during the
 | 
			
		||||
    third week, since testing and blocker resolution is unlikely
 | 
			
		||||
    to require all available resources.
 | 
			
		||||
* __Tag-up.__ Check in and status update among development team.
 | 
			
		||||
  May amend plan for sprint as-needed.
 | 
			
		||||
* __Code freeze.__ Any new work from this sprint
 | 
			
		||||
  (features, bug fixes, enhancements) must be integrated by the
 | 
			
		||||
  end of the second week of the sprint. After code freeze
 | 
			
		||||
  (and until the end of the sprint) the only changes that should be
 | 
			
		||||
  merged into the master branch should directly address issues
 | 
			
		||||
  needed to pass acceptance testing.
 | 
			
		||||
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
 | 
			
		||||
  Structured testing with predefined
 | 
			
		||||
  success criteria. No release should ship without passing
 | 
			
		||||
  acceptance tests. Time is allocated in each sprint for subsequent
 | 
			
		||||
  rounds of acceptance testing if issues are identified during a
 | 
			
		||||
  prior round. Specific details of acceptance testing need to be
 | 
			
		||||
  agreed-upon with relevant stakeholders and delivery recipients,
 | 
			
		||||
  and should be flexible enough to allow changes to plans
 | 
			
		||||
  (e.g. deferring delivery of some feature in order to ensure
 | 
			
		||||
  stability of other features.) Baseline testing includes:
 | 
			
		||||
  * [__Testathon.__](testing/plan.md#user-testing)
 | 
			
		||||
    Multi-user testing, involving as many users as
 | 
			
		||||
    is feasible, plus development team. Open-ended; should verify
 | 
			
		||||
    completed work from this sprint, test exploratorily for
 | 
			
		||||
    regressions, et cetera.
 | 
			
		||||
  * [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
 | 
			
		||||
    test to verify that the software remains
 | 
			
		||||
    stable after running for longer durations. May include some
 | 
			
		||||
    combination of automated testing and user verification (e.g.
 | 
			
		||||
    checking to verify that software remains subjectively
 | 
			
		||||
    responsive at conclusion of test.)
 | 
			
		||||
  * [__Unit Testing.__](testing/plan.md#unit-testing)
 | 
			
		||||
    Automated testing integrated into the
 | 
			
		||||
    build. (These tests are verified to pass more often than once
 | 
			
		||||
    per sprint, as they run before any merge to master, but still
 | 
			
		||||
    play an important role in per-release testing.)
 | 
			
		||||
* [__Per-sprint Testing.__](testing/plan.md#per-sprint-testing)
 | 
			
		||||
  Subset of Pre-release Testing
 | 
			
		||||
  which should be performed before shipping at the end of any
 | 
			
		||||
  sprint. Time is allocated for a second round of
 | 
			
		||||
  Pre-release Testing if the first round is not passed.
 | 
			
		||||
* __Triage.__ Team reviews issues from acceptance testing and uses
 | 
			
		||||
  success criteria to determine whether or not they should block
 | 
			
		||||
  release, then formulates a plan to address these issues before
 | 
			
		||||
  the next round of acceptance testing. Focus here should be on
 | 
			
		||||
  ensuring software passes that testing in order to ship on time;
 | 
			
		||||
  may prefer to disable malfunctioning components and fix them
 | 
			
		||||
  in a subsequent sprint, for example.
 | 
			
		||||
* __Ship.__ Tag a code snapshot that has passed acceptance
 | 
			
		||||
  testing and deploy that version. (Only true if acceptance
 | 
			
		||||
  testing has passed by this point; if acceptance testing has not
 | 
			
		||||
  been passed, will need to make ad hoc decisions with stakeholders,
 | 
			
		||||
  e.g. "extend the sprint" or "defer shipment until end of next
 | 
			
		||||
  sprint.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,156 +1,13 @@
 | 
			
		||||
# Development Cycle
 | 
			
		||||
 | 
			
		||||
Development of Open MCT Web occurs on an iterative cycle of
 | 
			
		||||
sprints and releases.
 | 
			
		||||
 | 
			
		||||
* A _sprint_ is three weeks in duration, and represents a
 | 
			
		||||
  set of improvements that can be completed and tested by the
 | 
			
		||||
  development team. Software at the end of the sprint is
 | 
			
		||||
  "semi-stable"; it will have undergone reduced testing and may carry
 | 
			
		||||
  defects or usability issues of lower severity, particularly if
 | 
			
		||||
  there are workarounds.
 | 
			
		||||
* A _release_ occurs every four sprints. Releases are stable, and
 | 
			
		||||
  will have undergone full acceptance testing to ensure that the
 | 
			
		||||
  software behaves correctly and usably.
 | 
			
		||||
 | 
			
		||||
## Roles
 | 
			
		||||
 | 
			
		||||
The sprint process assumes the presence of a __project manager.__
 | 
			
		||||
The project manager is responsible for
 | 
			
		||||
making tactical decisions about what development work will be
 | 
			
		||||
performed, and for coordinating with stakeholders to arrive at
 | 
			
		||||
higher-level strategic decisions about desired functionality
 | 
			
		||||
and characteristics of the software, major external milestones,
 | 
			
		||||
and so forth.
 | 
			
		||||
 | 
			
		||||
In the absence of a dedicated project manager, this role may be rotated
 | 
			
		||||
among members of the development team on a per-sprint basis.
 | 
			
		||||
 | 
			
		||||
Responsibilities of the project manager including:
 | 
			
		||||
 | 
			
		||||
* Maintaining (with agreement of stakeholders) a "road map" of work
 | 
			
		||||
  planned for future releases/sprints; this should be higher-level,
 | 
			
		||||
  usually expressed as "themes",
 | 
			
		||||
  with just enough specificity to gauge feasibility of plans,
 | 
			
		||||
  relate work back to milestones, and identify longer-term
 | 
			
		||||
  dependencies.
 | 
			
		||||
* Determining (with assistance from the rest of the team) which
 | 
			
		||||
  issues to work on in a given sprint and how they shall be
 | 
			
		||||
  assigned.
 | 
			
		||||
* Pre-planning subsequent sprints to ensure that all members of the
 | 
			
		||||
  team always have a clear direction.
 | 
			
		||||
* Scheduling and/or ensuring adherence to
 | 
			
		||||
  [process points](#process-points).
 | 
			
		||||
* Responding to changes within the sprint (shifting priorities,
 | 
			
		||||
  new issues) and re-allocating work for the sprint as needed.
 | 
			
		||||
 | 
			
		||||
## Sprint Calendar
 | 
			
		||||
 | 
			
		||||
Certain [process points](#process-points) are regularly scheduled in
 | 
			
		||||
the sprint cycle.
 | 
			
		||||
 | 
			
		||||
### Sprints by Release
 | 
			
		||||
 | 
			
		||||
Allocation of work among sprints should be planned relative to release
 | 
			
		||||
goals and milestones. As a general guideline, higher-risk work (large
 | 
			
		||||
new features which may carry new defects, major refactoring, design
 | 
			
		||||
changes with uncertain effects on usability) should be allocated to
 | 
			
		||||
earlier sprints, allowing for time in later sprints to ensure stability.
 | 
			
		||||
 | 
			
		||||
| Sprint | Focus                                                   |
 | 
			
		||||
|:------:|:--------------------------------------------------------|
 | 
			
		||||
| __1__  | Prototyping, design, experimentation.                   |
 | 
			
		||||
| __2__  | New features, refinements, enhancements.                |
 | 
			
		||||
| __3__  | Feature completion, low-risk enhancements, bug fixing.  |
 | 
			
		||||
| __4__  | Stability & quality assurance.                          |
 | 
			
		||||
 | 
			
		||||
### Sprints 1-3
 | 
			
		||||
 | 
			
		||||
The first three sprints of a release are primarily centered around
 | 
			
		||||
development work, with regular acceptance testing in the third
 | 
			
		||||
week. During this third week, the top priority should be passing
 | 
			
		||||
acceptance testing (e.g. by resolving any blockers found); any
 | 
			
		||||
resources not needed for this effort should be used to begin work
 | 
			
		||||
for the subsequent sprint.
 | 
			
		||||
 | 
			
		||||
| Week  | Mon                       | Tue    | Wed | Thu                          | Fri         |
 | 
			
		||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
 | 
			
		||||
| __1__ | Sprint plan               | Tag-up |     |                              |             |
 | 
			
		||||
| __2__ |                           | Tag-up |     |                              | Code freeze |
 | 
			
		||||
| __3__ | Sprint acceptance testing | Triage |     | _Sprint acceptance testing*_ | Ship        |
 | 
			
		||||
 | 
			
		||||
* If necessary.
 | 
			
		||||
 | 
			
		||||
### Sprint 4
 | 
			
		||||
 | 
			
		||||
The software must be stable at the end of the fourth sprint; because of
 | 
			
		||||
this, the fourth sprint is scheduled differently, with a heightened
 | 
			
		||||
emphasis on testing.
 | 
			
		||||
 | 
			
		||||
| Week   | Mon                       | Tue    | Wed | Thu                          | Fri         |
 | 
			
		||||
|-------:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
 | 
			
		||||
| __1__  | Sprint plan               | Tag-up |     |                              | Code freeze |
 | 
			
		||||
| __2__  | Acceptance testing        | Triage |     |                              |             |
 | 
			
		||||
| __3__  | _Acceptance testing*_     | Triage |     | _Acceptance testing*_        | Ship        |
 | 
			
		||||
 | 
			
		||||
* If necessary.
 | 
			
		||||
 | 
			
		||||
## Process Points
 | 
			
		||||
 | 
			
		||||
* __Sprint plan.__ Project manager allocates issues based on
 | 
			
		||||
  theme(s) for sprint, then reviews with team. Each team member
 | 
			
		||||
  should have roughly two weeks of work allocated (to allow time
 | 
			
		||||
  in the third week for testing of work completed.)
 | 
			
		||||
  * Project manager should also sketch out subsequent sprint so
 | 
			
		||||
    that team may begin work for that sprint during the
 | 
			
		||||
    third week, since testing and blocker resolution is unlikely
 | 
			
		||||
    to require all available resources.
 | 
			
		||||
* __Tag-up.__ Check in and status update among development team.
 | 
			
		||||
  May amend plan for sprint as-needed.
 | 
			
		||||
* __Code freeze.__ Any new work from this sprint
 | 
			
		||||
  (features, bug fixes, enhancements) must be integrated by the
 | 
			
		||||
  end of the second week of the sprint. After code freeze
 | 
			
		||||
  (and until the end of the sprint) the only changes that should be
 | 
			
		||||
  merged into the master branch should directly address issues
 | 
			
		||||
  needed to pass acceptance testing.
 | 
			
		||||
* __Acceptance Testing.__ Structured testing with predefined
 | 
			
		||||
  success criteria. No release should ship without passing
 | 
			
		||||
  acceptance tests. Time is allocated in each sprint for subsequent
 | 
			
		||||
  rounds of acceptance testing if issues are identified during a
 | 
			
		||||
  prior round. Specific details of acceptance testing need to be
 | 
			
		||||
  agreed-upon with relevant stakeholders and delivery recipients,
 | 
			
		||||
  and should be flexible enough to allow changes to plans
 | 
			
		||||
  (e.g. deferring delivery of some feature in order to ensure
 | 
			
		||||
  stability of other features.) Baseline testing includes:
 | 
			
		||||
  * __Testathon.__ Multi-user testing, involving as many users as
 | 
			
		||||
    is feasible, plus development team. Open-ended; should verify
 | 
			
		||||
    completed work from this sprint, test exploratorily for
 | 
			
		||||
    regressions, et cetera.
 | 
			
		||||
  * __24-Hour Test.__ A test to verify that the software remains
 | 
			
		||||
    stable after running for longer durations. May include some
 | 
			
		||||
    combination of automated testing and user verification (e.g.
 | 
			
		||||
    checking to verify that software remains subjectively
 | 
			
		||||
    responsive at conclusion of test.)
 | 
			
		||||
  * __Automated Testing.__ Automated testing integrated into the
 | 
			
		||||
    build. (These tests are verified to pass more often than once
 | 
			
		||||
    per sprint, as they run before any merge to master, but still
 | 
			
		||||
    play an important role in acceptance testing.)
 | 
			
		||||
* __Sprint Acceptance Testing.__ Subset of Acceptance Testing
 | 
			
		||||
  which should be performed before shipping at the end of any
 | 
			
		||||
  sprint. Time is allocated for a second round of
 | 
			
		||||
  Sprint Acceptance Testing if the first round is not passed.
 | 
			
		||||
* __Triage.__ Team reviews issues from acceptance testing and uses
 | 
			
		||||
  success criteria to determine whether or not they should block
 | 
			
		||||
  release, then formulates a plan to address these issues before
 | 
			
		||||
  the next round of acceptance testing. Focus here should be on
 | 
			
		||||
  ensuring software passes that testing in order to ship on time;
 | 
			
		||||
  may prefer to disable malfunctioning components and fix them
 | 
			
		||||
  in a subsequent sprint, for example.
 | 
			
		||||
* __Ship.__ Tag a code snapshot that has passed acceptance
 | 
			
		||||
  testing and deploy that version. (Only true if acceptance
 | 
			
		||||
  testing has passed by this point; if acceptance testing has not
 | 
			
		||||
  been passed, will need to make ad hoc decisions with stakeholders,
 | 
			
		||||
  e.g. "extend the sprint" or "defer shipment until end of next
 | 
			
		||||
  sprint.")
 | 
			
		||||
# Development Process
 | 
			
		||||
 | 
			
		||||
The process used to develop Open MCT Web is described in the following
 | 
			
		||||
documents:
 | 
			
		||||
 | 
			
		||||
* [Development Cycle](cycle.md): Describes how and when specific
 | 
			
		||||
  process points are repeated during development.
 | 
			
		||||
* Testing is described in two documents:
 | 
			
		||||
  * The [Test Plan](testing/plan.md) summarizes the approaches used
 | 
			
		||||
    to test Open MCT Web.
 | 
			
		||||
  * The [Test Procedures](testing/procedures.md) document what
 | 
			
		||||
    specific tests are performed to verify correctness, and how
 | 
			
		||||
    they should be carried out.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										127
									
								
								docs/src/process/testing/plan.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								docs/src/process/testing/plan.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
# Test Plan
 | 
			
		||||
 | 
			
		||||
## Test Levels
 | 
			
		||||
 | 
			
		||||
Testing for Open MCT Web includes:
 | 
			
		||||
 | 
			
		||||
* _Smoke testing_: Brief, informal testing to verify that no major issues
 | 
			
		||||
  or regressions are present in the software, or in specific features of
 | 
			
		||||
  the software.
 | 
			
		||||
* _Unit testing_: Automated verification of the performance of individual
 | 
			
		||||
  software components.
 | 
			
		||||
* _User testing_: Testing with a representative user base to verify
 | 
			
		||||
  that application behaves usably and as specified.
 | 
			
		||||
* _Long-duration testing_: Testing which takes place over a long period
 | 
			
		||||
  of time to detect issues which are not readily noticeable during
 | 
			
		||||
  shorter test periods.
 | 
			
		||||
 | 
			
		||||
### Smoke Testing
 | 
			
		||||
 | 
			
		||||
Manual, non-rigorous testing of the software and/or specific features
 | 
			
		||||
of interest. Verifies that the software runs and that basic functionality
 | 
			
		||||
is present.
 | 
			
		||||
 | 
			
		||||
### Unit Testing
 | 
			
		||||
 | 
			
		||||
Unit tests are automated tests which exercise individual software
 | 
			
		||||
components. Tests are subject to code review along with the actual
 | 
			
		||||
implementation, to ensure that tests are applicable and useful.
 | 
			
		||||
 | 
			
		||||
Unit tests should meet
 | 
			
		||||
[test standards](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#test-standards)
 | 
			
		||||
as described in the contributing guide.
 | 
			
		||||
 | 
			
		||||
### User Testing
 | 
			
		||||
 | 
			
		||||
User testing is performed at scheduled times involving target users
 | 
			
		||||
of the software or reasonable representatives, along with members of
 | 
			
		||||
the development team exercising known use cases. Users test the
 | 
			
		||||
software directly; the software should be configured as similarly to
 | 
			
		||||
its planned production configuration as is feasible without introducing
 | 
			
		||||
other risks (e.g. damage to data in a production instance.)
 | 
			
		||||
 | 
			
		||||
User testing will focus on the following activities:
 | 
			
		||||
 | 
			
		||||
* Verifying issues resolved since the last test session.
 | 
			
		||||
* Checking for regressions in areas related to recent changes.
 | 
			
		||||
* Using major or important features of the software,
 | 
			
		||||
  as determined by the user.
 | 
			
		||||
* General "trying to break things."
 | 
			
		||||
 | 
			
		||||
During user testing, users will
 | 
			
		||||
[report issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
 | 
			
		||||
as they are encountered.
 | 
			
		||||
 | 
			
		||||
Desired outcomes of user testing are:
 | 
			
		||||
 | 
			
		||||
* Identified software defects.
 | 
			
		||||
* Areas for usability improvement.
 | 
			
		||||
* Feature requests (particularly missed requirements.)
 | 
			
		||||
* Recorded issue verification.
 | 
			
		||||
 | 
			
		||||
### Long-duration Testing
 | 
			
		||||
 | 
			
		||||
Long-duration testing occurs over a twenty-four hour period. The
 | 
			
		||||
software is run in one or more stressing cases representative of expected
 | 
			
		||||
usage. After twenty-four hours, the software is evaluated for:
 | 
			
		||||
 | 
			
		||||
* Performance metrics: Have memory usage or CPU utilization increased
 | 
			
		||||
  during this time period in unexpected or undesirable ways?
 | 
			
		||||
* Subjective usability: Does the software behave in the same way it did
 | 
			
		||||
  at the start of the test? Is it as responsive?
 | 
			
		||||
 | 
			
		||||
Any defects or unexpected behavior identified during testing should be
 | 
			
		||||
[reported as issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
 | 
			
		||||
and reviewed for severity.
 | 
			
		||||
 | 
			
		||||
## Test Performance
 | 
			
		||||
 | 
			
		||||
Tests are performed at various levels of frequency.
 | 
			
		||||
 | 
			
		||||
* _Per-merge_: Performed before any new changes are integrated into
 | 
			
		||||
  the software.
 | 
			
		||||
* _Per-sprint_: Performed at the end of every [sprint](../cycle.md).
 | 
			
		||||
* _Per-release_: Performed at the end of every [release](../cycle.md).
 | 
			
		||||
 | 
			
		||||
### Per-merge Testing
 | 
			
		||||
 | 
			
		||||
Before changes are merged, the author of the changes must perform:
 | 
			
		||||
 | 
			
		||||
* _Smoke testing_ (both generally, and for areas which interact with
 | 
			
		||||
  the new changes.)
 | 
			
		||||
* _Unit testing_ (as part of the automated build step.)
 | 
			
		||||
 | 
			
		||||
Changes are not merged until the author has affirmed that both
 | 
			
		||||
forms of testing have been performed successfully; this is documented
 | 
			
		||||
by the [Author Checklist](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#author-checklist).
 | 
			
		||||
 | 
			
		||||
### Per-sprint Testing
 | 
			
		||||
 | 
			
		||||
Before a sprint is closed, the development team must additionally
 | 
			
		||||
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)
 | 
			
		||||
  (specifically, for 24 hours.)
 | 
			
		||||
 | 
			
		||||
Issues are reported as a product of both forms of testing.
 | 
			
		||||
 | 
			
		||||
A sprint is not closed until both categories have been performed on
 | 
			
		||||
the latest snapshot of the software, _and_ no issues labelled as
 | 
			
		||||
["blocker"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
 | 
			
		||||
remain open.
 | 
			
		||||
 | 
			
		||||
### Per-release Testing
 | 
			
		||||
 | 
			
		||||
As [per-sprint testing](#per-sprint-testing), except that _user testing_
 | 
			
		||||
should cover all test cases, with less focus on changes from the specific
 | 
			
		||||
sprint or release.
 | 
			
		||||
 | 
			
		||||
Per-release testing should also include any acceptance testing steps
 | 
			
		||||
agreed upon with recipients of the software.
 | 
			
		||||
 | 
			
		||||
A release is not closed until both categories have been performed on
 | 
			
		||||
the latest snapshot of the software, _and_ no issues labelled as
 | 
			
		||||
["blocker" or "critical"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
 | 
			
		||||
remain open.
 | 
			
		||||
							
								
								
									
										169
									
								
								docs/src/process/testing/procedures.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								docs/src/process/testing/procedures.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
# Test Procedures
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
This document is intended to be used:
 | 
			
		||||
 | 
			
		||||
* By testers, to verify that Open MCT Web behaves as specified.
 | 
			
		||||
* By the development team, to document new test cases and to provide
 | 
			
		||||
  guidance on how to author these.
 | 
			
		||||
 | 
			
		||||
## Writing Procedures
 | 
			
		||||
 | 
			
		||||
### Template
 | 
			
		||||
 | 
			
		||||
Procedures for individual tests should use the following template,
 | 
			
		||||
adapted from [https://swehb.nasa.gov/display/7150/SWE-114]().
 | 
			
		||||
 | 
			
		||||
Property       | Value
 | 
			
		||||
---------------|---------------------------------------------------------------
 | 
			
		||||
Test ID        |
 | 
			
		||||
Relevant reqs. |
 | 
			
		||||
Prerequisites  |
 | 
			
		||||
Test input     |
 | 
			
		||||
Instructions   |
 | 
			
		||||
Expectation    |
 | 
			
		||||
Eval. criteria |
 | 
			
		||||
 | 
			
		||||
For multi-line descriptions, use an asterisk or similar indicator to refer
 | 
			
		||||
to a longer-form description below.
 | 
			
		||||
 | 
			
		||||
#### Example Procedure - Edit a Layout
 | 
			
		||||
 | 
			
		||||
Property       | Value
 | 
			
		||||
---------------|---------------------------------------------------------------
 | 
			
		||||
Test ID        | MCT-TEST-000X - Edit a layout
 | 
			
		||||
Relevant reqs. | MCT-EDIT-000Y
 | 
			
		||||
Prerequisites  | Create a layout, as in MCT-TEST-000Z
 | 
			
		||||
Test input     | Domain object database XYZ
 | 
			
		||||
Instructions   | See below *
 | 
			
		||||
Expectation    | Change to editing context †
 | 
			
		||||
Eval. criteria | Visual inspection
 | 
			
		||||
 | 
			
		||||
* Follow the following steps:
 | 
			
		||||
 | 
			
		||||
1. Verify that the created layout is currently navigated-to,
 | 
			
		||||
   as in MCT-TEST-00ZZ.
 | 
			
		||||
2. Click the Edit button, identified by a pencil icon and the text "Edit"
 | 
			
		||||
   displayed on hover.
 | 
			
		||||
 | 
			
		||||
† Right-hand viewing area should be surrounded by a dashed
 | 
			
		||||
blue border when a domain object is being edited.
 | 
			
		||||
 | 
			
		||||
### Guidelines
 | 
			
		||||
 | 
			
		||||
Test procedures should be written assuming minimal prior knowledge of the
 | 
			
		||||
application: Non-standard terms should only be used when they are documented
 | 
			
		||||
in [the glossary](#glossary), and shorthands used for user actions should
 | 
			
		||||
be accompanied by useful references to test procedures describing those
 | 
			
		||||
actions (when available) or descriptions in user documentation.
 | 
			
		||||
 | 
			
		||||
Test cases should be narrow in scope; if a list of steps is excessively
 | 
			
		||||
long (or must be written vaguely to be kept short) it should be broken
 | 
			
		||||
down into multiple tests which reference one another.
 | 
			
		||||
 | 
			
		||||
All requirements satisfied by Open MCT Web should be verifiable using
 | 
			
		||||
one or more test procedures.
 | 
			
		||||
 | 
			
		||||
## Glossary
 | 
			
		||||
 | 
			
		||||
This section will contain terms used in test procedures. This may link to
 | 
			
		||||
a common glossary, to avoid replication of content.
 | 
			
		||||
 | 
			
		||||
## Procedures
 | 
			
		||||
 | 
			
		||||
This section will contain specific test procedures. Presently, procedures
 | 
			
		||||
are placeholders describing general patterns for setting up and conducting
 | 
			
		||||
testing.
 | 
			
		||||
 | 
			
		||||
### User Testing Setup
 | 
			
		||||
 | 
			
		||||
These procedures describes a general pattern for setting up for user
 | 
			
		||||
testing. Specific deployments should customize this pattern with
 | 
			
		||||
relevant data and any additional steps necessary.
 | 
			
		||||
 | 
			
		||||
Property       | Value
 | 
			
		||||
---------------|---------------------------------------------------------------
 | 
			
		||||
Test ID        | MCT-TEST-SETUP0 - User Testing Setup
 | 
			
		||||
Relevant reqs. | TBD
 | 
			
		||||
Prerequisites  | Build of relevant components
 | 
			
		||||
Test input     | Exemplary database; exemplary telemetry data set
 | 
			
		||||
Instructions   | See below
 | 
			
		||||
Expectation    | Able to load application in a web browser (Google Chrome)
 | 
			
		||||
Eval. criteria | Visual inspection
 | 
			
		||||
 | 
			
		||||
Instructions:
 | 
			
		||||
 | 
			
		||||
1. Start telemetry server.
 | 
			
		||||
2. Start ElasticSearch.
 | 
			
		||||
3. Restore database snapshot to ElasticSearch.
 | 
			
		||||
4. Start telemetry playback.
 | 
			
		||||
5. Start HTTP server for client sources.
 | 
			
		||||
 | 
			
		||||
### User Test Procedures
 | 
			
		||||
 | 
			
		||||
Specific user test cases have not yet been authored. In their absence,
 | 
			
		||||
user testing is conducted by:
 | 
			
		||||
 | 
			
		||||
* Reviewing the text of issues from the issue tracker to understand the
 | 
			
		||||
  desired behavior, and exercising this behavior in the running application.
 | 
			
		||||
  (For instance, by following steps to reproduce from the original issue.)
 | 
			
		||||
  * Issues which appear to be resolved should be marked as such with comments
 | 
			
		||||
    on the original issue (e.g. "verified during user testing MM/DD/YYYY".)
 | 
			
		||||
  * Issues which appear not to have been resolved should be reopened with an
 | 
			
		||||
    explanation of what unexpected behavior has been observed.
 | 
			
		||||
  * In cases where an issue appears resolved as-worded but other related
 | 
			
		||||
    undesirable behavior is observed during testing, a new issue should be
 | 
			
		||||
    opened, and linked to from a comment in the original issues.
 | 
			
		||||
* General usage of new features and/or existing features which have undergone
 | 
			
		||||
  recent changes. Defects or problems with usability should be documented
 | 
			
		||||
  by filing issues in the issue tracker.
 | 
			
		||||
* Open-ended testing to discover defects, identify usability issues, and
 | 
			
		||||
  generate feature requests.
 | 
			
		||||
 | 
			
		||||
### Long-Duration Testing
 | 
			
		||||
 | 
			
		||||
The purpose of long-duration testing is to identify performance issues
 | 
			
		||||
and/or other defects which are sensitive to the amount of time the
 | 
			
		||||
application is kept running. (Memory leaks, for instance.)
 | 
			
		||||
 | 
			
		||||
Property       | Value
 | 
			
		||||
---------------|---------------------------------------------------------------
 | 
			
		||||
Test ID        | MCT-TEST-LDT0 - Long-duration Testing
 | 
			
		||||
Relevant reqs. | TBD
 | 
			
		||||
Prerequisites  | MCT-TEST-SETUP0
 | 
			
		||||
Test input     | (As for test setup.)
 | 
			
		||||
Instructions   | See "Instructions" below *
 | 
			
		||||
Expectation    | See "Expectations" below †
 | 
			
		||||
Eval. criteria | Visual inspection
 | 
			
		||||
 | 
			
		||||
* Instructions:
 | 
			
		||||
 | 
			
		||||
1. Start `top` or a similar tool to measure CPU usage and memory utilization.
 | 
			
		||||
2. Open several user-created displays (as many as would be realistically
 | 
			
		||||
   opened during actual usage in a stressing case) in some combination of
 | 
			
		||||
   separate tabs and windows (approximately as many tabs-per-window as
 | 
			
		||||
   total windows.)
 | 
			
		||||
3. Ensure that playback data is set to run continuously for at least 24 hours
 | 
			
		||||
   (e.g. on a loop.)
 | 
			
		||||
4. Record CPU usage and memory utilization.
 | 
			
		||||
5. In at least one tab, try some general user interface gestures and make
 | 
			
		||||
   notes about the subjective experience of using the application. (Particularly,
 | 
			
		||||
   the degree of responsiveness.)
 | 
			
		||||
6. Leave client displays open for 24 hours.
 | 
			
		||||
7. Record CPU usage and memory utilization again.
 | 
			
		||||
8. Make additional notes about the subjective experience of using the
 | 
			
		||||
   application (again, particularly responsiveness.)
 | 
			
		||||
9. Check logs for any unexpected warnings or errors.
 | 
			
		||||
 | 
			
		||||
† Expectations:
 | 
			
		||||
 | 
			
		||||
* At the end of the test, CPU usage and memory usage should both be similar
 | 
			
		||||
  to their levels at the start of the test.
 | 
			
		||||
* At the end of the test, subjective usage of the application should not
 | 
			
		||||
  be observably different from the way it was at the start of the test.
 | 
			
		||||
  (In particular, responsiveness should not decrease.)
 | 
			
		||||
* Logs should not contain any unexpected warnings or errors ("expected"
 | 
			
		||||
  warnings or errors are those that have been documented and prioritized
 | 
			
		||||
  as known issues, or those that are explained by transient conditions
 | 
			
		||||
  external to the software, such as network outages.)
 | 
			
		||||
@@ -39,8 +39,11 @@ define(
 | 
			
		||||
                start = Date.now();
 | 
			
		||||
 | 
			
		||||
            function update() {
 | 
			
		||||
                var secs = (Date.now() - start) / 1000;
 | 
			
		||||
                var now = Date.now(),
 | 
			
		||||
                    secs = (now - start) / 1000;
 | 
			
		||||
                displayed = Math.round(digests / secs);
 | 
			
		||||
                start = now;
 | 
			
		||||
                digests = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function increment() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								example/scratchpad/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								example/scratchpad/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
Example of using multiple persistence stores by exposing a root
 | 
			
		||||
object with a different space prefix.
 | 
			
		||||
							
								
								
									
										23
									
								
								example/scratchpad/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								example/scratchpad/bundle.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "roots": [
 | 
			
		||||
            {
 | 
			
		||||
                "id": "scratch:root",
 | 
			
		||||
                "model": {
 | 
			
		||||
                    "type": "folder",
 | 
			
		||||
                    "composition": [],
 | 
			
		||||
                    "name": "Scratchpad"
 | 
			
		||||
                },
 | 
			
		||||
                "priority": "preferred"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "components": [
 | 
			
		||||
            {
 | 
			
		||||
                "provides": "persistenceService",
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "ScratchPersistenceProvider.js",
 | 
			
		||||
                "depends": [ "$q" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								example/scratchpad/src/ScratchPersistenceProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								example/scratchpad/src/ScratchPersistenceProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The ScratchPersistenceProvider keeps JSON documents in memory
 | 
			
		||||
         * and provides a persistence interface, but changes are lost on reload.
 | 
			
		||||
         * @memberof example/scratchpad
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {PersistenceService}
 | 
			
		||||
         * @param q Angular's $q, for promises
 | 
			
		||||
         */
 | 
			
		||||
        function ScratchPersistenceProvider($q) {
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.table = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ScratchPersistenceProvider.prototype.listSpaces = function () {
 | 
			
		||||
            return this.$q.when(['scratch']);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ScratchPersistenceProvider.prototype.listObjects = function (space) {
 | 
			
		||||
            return this.$q.when(
 | 
			
		||||
                space === 'scratch' ? Object.keys(this.table) : []
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ScratchPersistenceProvider.prototype.createObject = function (space, key, value) {
 | 
			
		||||
            if (space === 'scratch') {
 | 
			
		||||
                this.table[key] = JSON.stringify(value);
 | 
			
		||||
            }
 | 
			
		||||
            return this.$q.when(space === 'scratch');
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ScratchPersistenceProvider.prototype.readObject = function (space, key) {
 | 
			
		||||
            return this.$q.when(
 | 
			
		||||
                (space === 'scratch' && this.table[key]) ?
 | 
			
		||||
                        JSON.parse(this.table[key]) : undefined
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ScratchPersistenceProvider.prototype.deleteObject = function (space, key, value) {
 | 
			
		||||
            if (space === 'scratch') {
 | 
			
		||||
                delete this.table[key];
 | 
			
		||||
            }
 | 
			
		||||
            return this.$q.when(space === 'scratch');
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ScratchPersistenceProvider.prototype.updateObject =
 | 
			
		||||
            ScratchPersistenceProvider.prototype.createObject;
 | 
			
		||||
 | 
			
		||||
        return ScratchPersistenceProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										10
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								index.html
									
									
									
									
									
								
							@@ -27,12 +27,12 @@
 | 
			
		||||
    <title></title>
 | 
			
		||||
    <script type="text/javascript"
 | 
			
		||||
            src="platform/framework/lib/require.js"
 | 
			
		||||
            data-main="platform/framework/src/Main.js">
 | 
			
		||||
            data-main="main.js">
 | 
			
		||||
    </script>
 | 
			
		||||
	<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
 | 
			
		||||
	<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
 | 
			
		||||
	<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
 | 
			
		||||
	<link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
 | 
			
		||||
    <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
 | 
			
		||||
    <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
 | 
			
		||||
    <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
 | 
			
		||||
    <link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
 | 
			
		||||
</head>
 | 
			
		||||
<body class="user-environ" ng-view>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								main.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 require*/
 | 
			
		||||
(function (require, options) {
 | 
			
		||||
    'use strict';
 | 
			
		||||
    require.config({
 | 
			
		||||
        shim: {
 | 
			
		||||
            "platform/framework/lib/angular.min": {
 | 
			
		||||
                exports: "angular"
 | 
			
		||||
            },
 | 
			
		||||
            "platform/framework/lib/angular-route.min": {
 | 
			
		||||
                deps: ["platform/framework/lib/angular.min"]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    require(["platform/framework/src/Main"]);
 | 
			
		||||
}(require));
 | 
			
		||||
@@ -4,6 +4,8 @@
 | 
			
		||||
  "description": "The OpenMCTWeb core platform",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.13.1",
 | 
			
		||||
    "globby": "^4.0.0",
 | 
			
		||||
    "requirejs": "^2.1.17",
 | 
			
		||||
    "minimist": "^1.1.1"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
@@ -16,13 +18,13 @@
 | 
			
		||||
    "karma-jasmine": "^0.1.5",
 | 
			
		||||
    "karma-phantomjs-launcher": "^0.1.4",
 | 
			
		||||
    "karma-requirejs": "^0.2.2",
 | 
			
		||||
    "requirejs": "^2.1.17",
 | 
			
		||||
    "marked": "^0.3.5",
 | 
			
		||||
    "glob": ">= 3.0.0",
 | 
			
		||||
    "split": "^1.0.0",
 | 
			
		||||
    "mkdirp": "^0.5.1",
 | 
			
		||||
    "nomnoml": "^0.0.3",
 | 
			
		||||
    "canvas": "^1.2.7",
 | 
			
		||||
    "strip-html-comments": "^1.0.0",
 | 
			
		||||
    "markdown-toc": "^0.11.7"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@@ -32,7 +34,8 @@
 | 
			
		||||
    "watch": "karma start",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api",
 | 
			
		||||
    "otherdoc": "node docs/gendocs.js --in docs/src --out target/docs",
 | 
			
		||||
    "docs": "npm run jsdoc ; npm run otherdoc"
 | 
			
		||||
    "docs": "npm run jsdoc ; npm run otherdoc",
 | 
			
		||||
    "postinstall": "node build.js"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
 
 | 
			
		||||
@@ -102,6 +102,12 @@
 | 
			
		||||
                "implementation": "navigation/NavigationService.js"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "policies": [
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "creation/CreationPolicy.js",
 | 
			
		||||
                "category": "creation"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "actions": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "navigate",
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            // Introduce one create action per type
 | 
			
		||||
            return this.typeService.listTypes().filter(function (type) {
 | 
			
		||||
                return type.hasFeature("creation");
 | 
			
		||||
                return self.policyService.allow("creation", type);
 | 
			
		||||
            }).map(function (type) {
 | 
			
		||||
                return new CreateAction(
 | 
			
		||||
                    type,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								platform/commonUI/browse/src/creation/CreationPolicy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								platform/commonUI/browse/src/creation/CreationPolicy.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A policy for determining whether objects of a given type can be
 | 
			
		||||
         * created.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Policy}
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         */
 | 
			
		||||
        function CreationPolicy() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CreationPolicy.prototype.allow = function (type) {
 | 
			
		||||
            return type.hasFeature("creation");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CreationPolicy;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -33,6 +33,9 @@ define(
 | 
			
		||||
            var mockTypeService,
 | 
			
		||||
                mockDialogService,
 | 
			
		||||
                mockCreationService,
 | 
			
		||||
                mockPolicyService,
 | 
			
		||||
                mockCreationPolicy,
 | 
			
		||||
                mockPolicyMap = {},
 | 
			
		||||
                mockTypes,
 | 
			
		||||
                provider;
 | 
			
		||||
 | 
			
		||||
@@ -67,14 +70,32 @@ define(
 | 
			
		||||
                    "creationService",
 | 
			
		||||
                    [ "createObject" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockPolicyService = jasmine.createSpyObj(
 | 
			
		||||
                    "policyService",
 | 
			
		||||
                    [ "allow" ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockTypes = [ "A", "B", "C" ].map(createMockType);
 | 
			
		||||
 | 
			
		||||
                mockTypes.forEach(function(type){
 | 
			
		||||
                    mockPolicyMap[type.getName()] = true;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockCreationPolicy = function(type){
 | 
			
		||||
                    return mockPolicyMap[type.getName()];
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mockPolicyService.allow.andCallFake(function(category, type){
 | 
			
		||||
                    return category === "creation" && mockCreationPolicy(type) ? true : false;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockTypeService.listTypes.andReturn(mockTypes);
 | 
			
		||||
 | 
			
		||||
                provider = new CreateActionProvider(
 | 
			
		||||
                    mockTypeService,
 | 
			
		||||
                    mockDialogService,
 | 
			
		||||
                    mockCreationService
 | 
			
		||||
                    mockCreationService,
 | 
			
		||||
                    mockPolicyService
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@@ -94,15 +115,15 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("does not expose non-creatable types", function () {
 | 
			
		||||
                // One of the types won't have the creation feature...
 | 
			
		||||
                mockTypes[1].hasFeature.andReturn(false);
 | 
			
		||||
                mockPolicyMap[mockTypes[0].getName()] = false;
 | 
			
		||||
                // ...so it should have been filtered out.
 | 
			
		||||
                expect(provider.getActions({
 | 
			
		||||
                    key: "create",
 | 
			
		||||
                    domainObject: {}
 | 
			
		||||
                }).length).toEqual(2);
 | 
			
		||||
                // Make sure it was creation which was used to check
 | 
			
		||||
                expect(mockTypes[1].hasFeature)
 | 
			
		||||
                    .toHaveBeenCalledWith("creation");
 | 
			
		||||
                expect(mockPolicyService.allow)
 | 
			
		||||
                    .toHaveBeenCalledWith("creation", mockTypes[0]);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								platform/commonUI/browse/test/creation/CreationPolicySpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								platform/commonUI/browse/test/creation/CreationPolicySpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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/creation/CreationPolicy"],
 | 
			
		||||
    function (CreationPolicy) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The creation policy", function () {
 | 
			
		||||
            var mockType,
 | 
			
		||||
                policy;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockType = jasmine.createSpyObj(
 | 
			
		||||
                    'type',
 | 
			
		||||
                    ['hasFeature']
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                policy = new CreationPolicy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows creation of types with the creation feature", function () {
 | 
			
		||||
                mockType.hasFeature.andReturn(true);
 | 
			
		||||
                expect(policy.allow(mockType)).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("disallows creation of types without the creation feature", function () {
 | 
			
		||||
                mockType.hasFeature.andReturn(false);
 | 
			
		||||
                expect(policy.allow(mockType)).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
    "creation/CreateMenuController",
 | 
			
		||||
    "creation/CreateWizard",
 | 
			
		||||
    "creation/CreationService",
 | 
			
		||||
    "creation/CreationPolicy",
 | 
			
		||||
    "creation/LocatorController",
 | 
			
		||||
    "navigation/NavigateAction",
 | 
			
		||||
    "navigation/NavigationService",
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ define(
 | 
			
		||||
            // Simply trigger refresh of in-view objects; do not
 | 
			
		||||
            // write anything to database.
 | 
			
		||||
            persistence.persist = function () {
 | 
			
		||||
                cache.markDirty(editableObject);
 | 
			
		||||
                return cache.markDirty(editableObject);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Delegate refresh to the original object; this avoids refreshing
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,7 @@ define(
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.markDirty = function (domainObject) {
 | 
			
		||||
            this.dirtyObjects[domainObject.getId()] = domainObject;
 | 
			
		||||
            return this.$q.when(true);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ define(
 | 
			
		||||
                mockEditableObject,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockCache,
 | 
			
		||||
                mockPromise,
 | 
			
		||||
                capability;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
@@ -50,7 +51,9 @@ define(
 | 
			
		||||
                    "cache",
 | 
			
		||||
                    [ "markDirty" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockPromise = jasmine.createSpyObj("promise", ["then"]);
 | 
			
		||||
 | 
			
		||||
                mockCache.markDirty.andReturn(mockPromise);
 | 
			
		||||
                mockDomainObject.getCapability.andReturn(mockPersistence);
 | 
			
		||||
 | 
			
		||||
                capability = new EditablePersistenceCapability(
 | 
			
		||||
@@ -84,6 +87,10 @@ define(
 | 
			
		||||
                expect(mockPersistence.refresh).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("returns a promise from persist", function () {
 | 
			
		||||
                expect(capability.persist().then).toEqual(jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -19,6 +19,10 @@
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "StyleSheetLoader.js",
 | 
			
		||||
                "depends": [ "stylesheets[]", "$document", "THEME" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "UnsupportedBrowserWarning.js",
 | 
			
		||||
                "depends": [ "notificationService", "agentService" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "stylesheets": [
 | 
			
		||||
@@ -225,10 +229,6 @@
 | 
			
		||||
                "templateUrl": "templates/subtree.html",
 | 
			
		||||
                "uses": [ "composition" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "test",
 | 
			
		||||
                "templateUrl": "templates/test.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "tree-node",
 | 
			
		||||
                "templateUrl": "templates/tree-node.html",
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
	    margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
 | 
			
		||||
    }
 | 
			
		||||
	padding: 0 $interiorMargin;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
	position: relative;
 | 
			
		||||
    line-height: $formInputH;
 | 
			
		||||
	select {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,51 +34,31 @@ $mobileTreeItemH: 35px;
 | 
			
		||||
$mobileTreeItemIndent: 20px;
 | 
			
		||||
$mobileTreeRightArrowW: 30px;
 | 
			
		||||
 | 
			
		||||
/************************** WINDOW DIMENSIONS FOR RWD */
 | 
			
		||||
$phoMaxW: 514px;
 | 
			
		||||
$phoMaxH: 740px;
 | 
			
		||||
 | 
			
		||||
$tabMinW: 515px;
 | 
			
		||||
$tabMaxW: 799px;
 | 
			
		||||
 | 
			
		||||
$tabMinH: 741px;
 | 
			
		||||
$tabMaxH: 1024px;
 | 
			
		||||
 | 
			
		||||
$compMinW: 800px;
 | 
			
		||||
$compMinH: 1025px;
 | 
			
		||||
/************************** DEVICE WIDTHS */
 | 
			
		||||
// IMPORTANT! Usage assumes that ranges are mutually exclusive and have no gaps
 | 
			
		||||
$phoMaxW: 767px;
 | 
			
		||||
$tabMinW: 768px;
 | 
			
		||||
$tabMaxW: 1024px;
 | 
			
		||||
$desktopMinW: 1025px;
 | 
			
		||||
 | 
			
		||||
/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
 | 
			
		||||
$screenPortrait: "screen and (orientation: portrait)";
 | 
			
		||||
$screenLandscape: "screen and (orientation: landscape)";
 | 
			
		||||
$screenPortrait: "(orientation: portrait)";
 | 
			
		||||
$screenLandscape: "(orientation: landscape)";
 | 
			
		||||
 | 
			
		||||
$mobileDevice: "(max-device-width: #{$tabMaxW}) and (max-device-height: #{$tabMaxH})";
 | 
			
		||||
$mobileDeviceEmu: "(max-device-width: #{$tabMaxH}) and (max-device-height: #{$tabMaxW})";
 | 
			
		||||
//$mobileDevice: "(max-device-width: #{$tabMaxW})";
 | 
			
		||||
 | 
			
		||||
$phonePortraitCheck: "(max-width: #{$phoMaxW}) and (max-height: #{$phoMaxH})";
 | 
			
		||||
$phoneLandscapeCheck: "(max-height: #{$phoMaxW}) and (max-width: #{$phoMaxH})";
 | 
			
		||||
 | 
			
		||||
$tabWidPorCheck: "(min-width: #{$tabMinW}) and (max-width: #{$tabMaxW})";
 | 
			
		||||
$tabHeiPorCheck: "(min-height: #{$tabMinH}) and (max-height: #{$tabMaxH})";
 | 
			
		||||
$tabletPortraitCheck: "#{$tabWidPorCheck} and #{$tabHeiPorCheck}";
 | 
			
		||||
 | 
			
		||||
$tabWidLanCheck: "(min-height: #{$tabMinW}) and (max-height: #{$tabMaxW})";
 | 
			
		||||
$tabHeiLanCheck: "(min-width: #{$tabMinH}) and (max-width: #{$tabMaxH})";
 | 
			
		||||
$tabletLandscapeCheck: "#{$tabWidLanCheck} and #{$tabHeiLanCheck}";
 | 
			
		||||
 | 
			
		||||
$desktopPortraitCheck: "(min-device-width: #{$compMinW}) and (min-device-height: #{$compMinH})";
 | 
			
		||||
$desktopLandscapeCheck: "(min-device-width: #{$compMinH}) and (min-device-height: #{$compMinW})";
 | 
			
		||||
$phoneCheck: "(max-device-width: #{$phoMaxW})";
 | 
			
		||||
$tabletCheck: "(min-device-width: #{$tabMinW}) and (max-device-width: #{$tabMaxW})";
 | 
			
		||||
$desktopCheck: "(min-device-width: #{$desktopMinW}) and (-webkit-min-device-pixel-ratio: 1)";
 | 
			
		||||
 | 
			
		||||
/************************** MEDIA QUERIES: WINDOWS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
 | 
			
		||||
$phonePortrait: "#{$screenPortrait} and #{$phonePortraitCheck} and #{$mobileDevice}";
 | 
			
		||||
$phoneLandscape: "#{$screenLandscape} and #{$phoneLandscapeCheck} and #{$mobileDevice}";
 | 
			
		||||
$phoneLandscapeEmu: "#{$screenLandscape} and #{$phoneLandscapeCheck} and #{$mobileDeviceEmu}";
 | 
			
		||||
$phonePortrait: "only screen and #{$screenPortrait} and #{$phoneCheck}";
 | 
			
		||||
$phoneLandscape: "only screen and #{$screenLandscape} and #{$phoneCheck}";
 | 
			
		||||
 | 
			
		||||
$tabletPortrait: "#{$screenPortrait} and #{$tabletPortraitCheck} and #{$mobileDevice}";
 | 
			
		||||
$tabletLandscape: "#{$screenLandscape} and #{$tabletLandscapeCheck} and #{$mobileDevice}";
 | 
			
		||||
$tabletLandscapeEmu: "#{$screenLandscape} and #{$tabletLandscapeCheck} and #{$mobileDeviceEmu}";
 | 
			
		||||
$tabletPortrait: "only screen and #{$screenPortrait} and #{$tabletCheck}";
 | 
			
		||||
$tabletLandscape: "only screen and #{$screenLandscape} and #{$tabletCheck}";
 | 
			
		||||
 | 
			
		||||
$desktopPortrait: "screen and #{$desktopPortraitCheck}";
 | 
			
		||||
$desktopLandscape: "screen and #{$desktopLandscapeCheck}";
 | 
			
		||||
$desktop: "only screen and #{$desktopCheck}";
 | 
			
		||||
 | 
			
		||||
/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */
 | 
			
		||||
$proporMenuOnly: 90%;
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,7 @@
 | 
			
		||||
// Phones in any orientation
 | 
			
		||||
@mixin phone {
 | 
			
		||||
    @media #{$phonePortrait},
 | 
			
		||||
           #{$phoneLandscape},
 | 
			
		||||
           #{$phoneLandscapeEmu} {
 | 
			
		||||
           #{$phoneLandscape} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -40,8 +39,7 @@
 | 
			
		||||
 | 
			
		||||
// Phones in landscape orientation
 | 
			
		||||
@mixin phoneLandscape {
 | 
			
		||||
    @media #{$phoneLandscape},
 | 
			
		||||
           #{$phoneLandscapeEmu} {
 | 
			
		||||
    @media #{$phoneLandscape} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -49,8 +47,7 @@
 | 
			
		||||
// Tablets in any orientation
 | 
			
		||||
@mixin tablet {
 | 
			
		||||
    @media #{$tabletPortrait},
 | 
			
		||||
           #{$tabletLandscape},
 | 
			
		||||
           #{$tabletLandscapeEmu} {
 | 
			
		||||
           #{$tabletLandscape} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -64,8 +61,7 @@
 | 
			
		||||
 | 
			
		||||
// Tablets in landscape orientation
 | 
			
		||||
@mixin tabletLandscape {
 | 
			
		||||
    @media #{$tabletLandscape},
 | 
			
		||||
           #{$tabletLandscapeEmu} {
 | 
			
		||||
    @media #{$tabletLandscape} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -74,10 +70,8 @@
 | 
			
		||||
@mixin phoneandtablet {
 | 
			
		||||
    @media #{$phonePortrait},
 | 
			
		||||
           #{$phoneLandscape},
 | 
			
		||||
           #{$phoneLandscapeEmu},
 | 
			
		||||
           #{$tabletPortrait},
 | 
			
		||||
           #{$tabletLandscape},
 | 
			
		||||
           #{$tabletLandscapeEmu} {
 | 
			
		||||
           #{$tabletLandscape} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -86,17 +80,14 @@
 | 
			
		||||
@mixin desktopandtablet {
 | 
			
		||||
    @media #{$tabletPortrait},
 | 
			
		||||
           #{$tabletLandscape},
 | 
			
		||||
           #{$tabletLandscapeEmu},
 | 
			
		||||
           #{$desktopPortrait},
 | 
			
		||||
           #{$desktopLandscape} {
 | 
			
		||||
           #{$desktop} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Desktop monitors in any orientation
 | 
			
		||||
@mixin desktop {
 | 
			
		||||
    @media #{$desktopPortrait},
 | 
			
		||||
           #{$desktopLandscape} {
 | 
			
		||||
    @media #{$desktop} {
 | 
			
		||||
        @content
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,9 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
$yBarW: 60px;
 | 
			
		||||
$yLabelW: auto;
 | 
			
		||||
$yLabelW: 10px;
 | 
			
		||||
$xBarH: 32px;
 | 
			
		||||
$legendH: 20px;
 | 
			
		||||
//$colorHash: rgba(white, 0.3); // MOVED INTO CONSTANTS
 | 
			
		||||
//$styleHash: dashed; // MOVED INTO CONSTANTS
 | 
			
		||||
$swatchD: 8px;
 | 
			
		||||
$plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBarW); // Top, right, bottom, left
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +34,6 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
	height: 100%;
 | 
			
		||||
 | 
			
		||||
	.gl-plot-axis-area {
 | 
			
		||||
		//		@include test(green);
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		&.gl-plot-x {
 | 
			
		||||
			top: auto;
 | 
			
		||||
@@ -59,7 +56,7 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
	.gl-plot-coords {
 | 
			
		||||
		@include box-sizing(border-box);
 | 
			
		||||
		@include border-radius($controlCr);
 | 
			
		||||
		background: black; //rgba($colorKey, 0.5);
 | 
			
		||||
		background: black;
 | 
			
		||||
		color: lighten($colorBodyFg, 30%);
 | 
			
		||||
		padding: 2px 5px;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
@@ -88,11 +85,9 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
 | 
			
		||||
	.gl-plot-label,
 | 
			
		||||
	.l-plot-label {
 | 
			
		||||
		//		@include test(yellow);
 | 
			
		||||
		color: $colorPlotLabelFg;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
//		text-transform: uppercase;
 | 
			
		||||
 | 
			
		||||
		&.gl-plot-x-label,
 | 
			
		||||
		&.l-plot-x-label {
 | 
			
		||||
@@ -117,20 +112,26 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.gl-plot-y-options {
 | 
			
		||||
	.gl-plot-x-options,
 | 
			
		||||
    .gl-plot-y-options {
 | 
			
		||||
		$h: 32px;
 | 
			
		||||
//		@include test();
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: 50%;
 | 
			
		||||
		right: auto;
 | 
			
		||||
		bottom: auto;
 | 
			
		||||
		left: $yLabelW + $interiorMargin;
 | 
			
		||||
		margin-top: $h / -2;
 | 
			
		||||
		height: auto;
 | 
			
		||||
		min-height: $h;
 | 
			
		||||
		width: $h;
 | 
			
		||||
        z-index: 2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    .gl-plot-x-options {
 | 
			
		||||
        top: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .gl-plot-y-options {
 | 
			
		||||
        @include transform(translateY(-50%));
 | 
			
		||||
        min-width: 150px; // Need this due to enclosure of .select
 | 
			
		||||
        top: 50%;
 | 
			
		||||
        left: $yLabelW + $interiorMargin * 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	.gl-plot-hash {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		border: 0 $colorPlotHash $stylePlotHash;
 | 
			
		||||
@@ -214,21 +215,13 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			height: $swatchD;
 | 
			
		||||
			width: $swatchD;
 | 
			
		||||
			//margin-right: $interiorMarginSm;
 | 
			
		||||
		}
 | 
			
		||||
		&[class*='s-limit'] {
 | 
			
		||||
			.title-label {
 | 
			
		||||
				//color: #fff;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gl-plot-legend {
 | 
			
		||||
	.plot-legend-item {
 | 
			
		||||
		//@include test();
 | 
			
		||||
		@include border-radius($smallCr);
 | 
			
		||||
		//color: #fff;
 | 
			
		||||
		line-height: 1.5em;
 | 
			
		||||
		padding: 0px $itemPadLR;
 | 
			
		||||
		.plot-color-swatch {
 | 
			
		||||
@@ -250,7 +243,6 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
 | 
			
		||||
.gl-plot-tick,
 | 
			
		||||
.tick-label {
 | 
			
		||||
	//			@include test(red);
 | 
			
		||||
	font-size: 0.7rem;
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
@@ -277,7 +269,6 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gl-plot-tick {
 | 
			
		||||
	//			@include test(red);
 | 
			
		||||
	&.gl-plot-x-tick-label {
 | 
			
		||||
		top: $interiorMargin;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,29 @@
 | 
			
		||||
<!--
 | 
			
		||||
 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.
 | 
			
		||||
-->
 | 
			
		||||
<span class="s-btn"
 | 
			
		||||
      ng-controller="DateTimeFieldController">
 | 
			
		||||
    <input type="text"
 | 
			
		||||
           ng-model="textValue"
 | 
			
		||||
           ng-blur="restoreTextValue(); ngBlur()"
 | 
			
		||||
           ng-class="{ error: textInvalid }">
 | 
			
		||||
    </input>
 | 
			
		||||
    <a class="ui-symbol icon icon-calendar"
 | 
			
		||||
@@ -11,8 +33,8 @@
 | 
			
		||||
    <mct-popup ng-if="picker.active">
 | 
			
		||||
        <div mct-click-elsewhere="picker.active = false">
 | 
			
		||||
            <mct-control key="'datetime-picker'"
 | 
			
		||||
                         ng-model="ngModel"
 | 
			
		||||
                         field="field"
 | 
			
		||||
                         ng-model="pickerModel"
 | 
			
		||||
                         field="'value'"
 | 
			
		||||
                         options="{ hours: true }">
 | 
			
		||||
            </mct-control>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,14 @@
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div ng-controller="TimeRangeController">
 | 
			
		||||
    <div class="l-time-range-inputs-holder">
 | 
			
		||||
    <form class="l-time-range-inputs-holder"
 | 
			
		||||
          ng-submit="updateBoundsFromForm()">
 | 
			
		||||
        <span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
 | 
			
		||||
        <span class="l-time-range-input">
 | 
			
		||||
            <mct-control key="'datetime-field'"
 | 
			
		||||
                         structure="{ format: parameters.format }"
 | 
			
		||||
                         ng-model="ngModel.outer"
 | 
			
		||||
                         ng-model="formModel"
 | 
			
		||||
                         ng-blur="updateBoundsFromForm()"
 | 
			
		||||
                         field="'start'"
 | 
			
		||||
                         class="time-range-start">
 | 
			
		||||
            </mct-control>
 | 
			
		||||
@@ -36,12 +38,15 @@
 | 
			
		||||
        <span class="l-time-range-input" ng-controller="ToggleController as t2">
 | 
			
		||||
            <mct-control key="'datetime-field'"
 | 
			
		||||
                         structure="{ format: parameters.format }"
 | 
			
		||||
                         ng-model="ngModel.outer"
 | 
			
		||||
                         ng-model="formModel"
 | 
			
		||||
                         ng-blur="updateBoundsFromForm()"
 | 
			
		||||
                         field="'end'"
 | 
			
		||||
                         class="time-range-end">
 | 
			
		||||
            </mct-control> 
 | 
			
		||||
        </span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
        <input type="submit" class="hidden">
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
    <div class="l-time-range-slider-holder">
 | 
			
		||||
        <div class="l-time-range-slider">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								platform/commonUI/general/src/UnsupportedBrowserWarning.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								platform/commonUI/general/src/UnsupportedBrowserWarning.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This bundle provides various general-purpose UI elements, including
 | 
			
		||||
 * platform styling.
 | 
			
		||||
 * @namespace platform/commonUI/general
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var WARNING_TITLE = "Unsupported browser",
 | 
			
		||||
            WARNING_DESCRIPTION = [
 | 
			
		||||
                "This software has been developed and tested",
 | 
			
		||||
                "using the latest Google Chrome,",
 | 
			
		||||
                "and may be unstable in other browsers."
 | 
			
		||||
            ].join(" "),
 | 
			
		||||
            MOBILE_BROWSER = "Safari",
 | 
			
		||||
            DESKTOP_BROWSER = "Chrome";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Shows a warning if a user's browser is unsupported.
 | 
			
		||||
         * @memberof platform/commonUI/general
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {NotificationService} notificationService the notification
 | 
			
		||||
         *        service
 | 
			
		||||
         */
 | 
			
		||||
        function UnsupportedBrowserWarning(notificationService, agentService) {
 | 
			
		||||
            var testToBrowser = agentService.isMobile() ?
 | 
			
		||||
                    MOBILE_BROWSER : DESKTOP_BROWSER;
 | 
			
		||||
 | 
			
		||||
            if (!agentService.isBrowser(testToBrowser)) {
 | 
			
		||||
                notificationService.alert({
 | 
			
		||||
                    title: WARNING_TITLE,
 | 
			
		||||
                    actionText: WARNING_DESCRIPTION
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return UnsupportedBrowserWarning;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -53,7 +53,9 @@ define(
 | 
			
		||||
                        formatter.parse($scope.textValue) !== value) {
 | 
			
		||||
                    $scope.textValue = formatter.format(value);
 | 
			
		||||
                    $scope.textInvalid = false;
 | 
			
		||||
                    $scope.lastValidValue = $scope.textValue;
 | 
			
		||||
                }
 | 
			
		||||
                $scope.pickerModel = { value: value };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateFromView(textValue) {
 | 
			
		||||
@@ -61,6 +63,17 @@ define(
 | 
			
		||||
                if (!$scope.textInvalid) {
 | 
			
		||||
                    $scope.ngModel[$scope.field] =
 | 
			
		||||
                        formatter.parse(textValue);
 | 
			
		||||
                    $scope.lastValidValue = $scope.textValue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateFromPicker(value) {
 | 
			
		||||
                if (value !== $scope.ngModel[$scope.field]) {
 | 
			
		||||
                    $scope.ngModel[$scope.field] = value;
 | 
			
		||||
                    updateFromModel(value);
 | 
			
		||||
                    if ($scope.ngBlur) {
 | 
			
		||||
                        $scope.ngBlur();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -69,10 +82,18 @@ define(
 | 
			
		||||
                updateFromModel($scope.ngModel[$scope.field]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function restoreTextValue() {
 | 
			
		||||
                $scope.textValue = $scope.lastValidValue;
 | 
			
		||||
                updateFromView($scope.textValue);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $scope.restoreTextValue = restoreTextValue;
 | 
			
		||||
 | 
			
		||||
            $scope.picker = { active: false };
 | 
			
		||||
 | 
			
		||||
            $scope.$watch('structure.format', setFormat);
 | 
			
		||||
            $scope.$watch('ngModel[field]', updateFromModel);
 | 
			
		||||
            $scope.$watch('pickerModel.value', updateFromPicker);
 | 
			
		||||
            $scope.$watch('textValue', updateFromView);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -175,6 +175,13 @@ define(
 | 
			
		||||
                updateViewFromModel($scope.ngModel);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateFormModel() {
 | 
			
		||||
                $scope.formModel = {
 | 
			
		||||
                    start: (($scope.ngModel || {}).outer || {}).start,
 | 
			
		||||
                    end: (($scope.ngModel || {}).outer || {}).end
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateOuterStart(t) {
 | 
			
		||||
                var ngModel = $scope.ngModel;
 | 
			
		||||
 | 
			
		||||
@@ -192,6 +199,7 @@ define(
 | 
			
		||||
                    ngModel.inner.end
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                updateFormModel();
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
@@ -213,6 +221,7 @@ define(
 | 
			
		||||
                    ngModel.inner.start
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                updateFormModel();
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
@@ -223,6 +232,14 @@ define(
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateBoundsFromForm() {
 | 
			
		||||
                $scope.ngModel = $scope.ngModel || {};
 | 
			
		||||
                $scope.ngModel.outer = {
 | 
			
		||||
                    start: $scope.formModel.start,
 | 
			
		||||
                    end: $scope.formModel.end
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $scope.startLeftDrag = startLeftDrag;
 | 
			
		||||
            $scope.startRightDrag = startRightDrag;
 | 
			
		||||
            $scope.startMiddleDrag = startMiddleDrag;
 | 
			
		||||
@@ -230,10 +247,13 @@ define(
 | 
			
		||||
            $scope.rightDrag = rightDrag;
 | 
			
		||||
            $scope.middleDrag = middleDrag;
 | 
			
		||||
 | 
			
		||||
            $scope.updateBoundsFromForm = updateBoundsFromForm;
 | 
			
		||||
 | 
			
		||||
            $scope.ticks = [];
 | 
			
		||||
 | 
			
		||||
            // Initialize scope to defaults
 | 
			
		||||
            updateViewFromModel($scope.ngModel);
 | 
			
		||||
            updateFormModel();
 | 
			
		||||
 | 
			
		||||
            $scope.$watchCollection("ngModel", updateViewFromModel);
 | 
			
		||||
            $scope.$watch("spanWidth", updateSpanWidth);
 | 
			
		||||
 
 | 
			
		||||
@@ -204,7 +204,7 @@ define(
 | 
			
		||||
                // And poll for position changes enforced by styles
 | 
			
		||||
                activeInterval = $interval(function () {
 | 
			
		||||
                    getSetPosition(getSetPosition());
 | 
			
		||||
                }, POLLING_INTERVAL, false);
 | 
			
		||||
                }, POLLING_INTERVAL, 0, false);
 | 
			
		||||
 | 
			
		||||
                // ...and stop polling when we're destroyed.
 | 
			
		||||
                $scope.$on('$destroy', function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,98 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/UnsupportedBrowserWarning"],
 | 
			
		||||
    function (UnsupportedBrowserWarning) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var MOBILE_BROWSER = "Safari",
 | 
			
		||||
            DESKTOP_BROWSER = "Chrome",
 | 
			
		||||
            UNSUPPORTED_BROWSERS = [
 | 
			
		||||
                "Firefox",
 | 
			
		||||
                "IE",
 | 
			
		||||
                "Opera",
 | 
			
		||||
                "Iceweasel"
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
        describe("The unsupported browser warning", function () {
 | 
			
		||||
            var mockNotificationService,
 | 
			
		||||
                mockAgentService,
 | 
			
		||||
                testAgent;
 | 
			
		||||
 | 
			
		||||
            function instantiateWith(browser) {
 | 
			
		||||
                testAgent = "Mozilla/5.0 " + browser + "/12.34.56";
 | 
			
		||||
                return new UnsupportedBrowserWarning(
 | 
			
		||||
                    mockNotificationService,
 | 
			
		||||
                    mockAgentService
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testAgent = "chrome";
 | 
			
		||||
                mockNotificationService = jasmine.createSpyObj(
 | 
			
		||||
                    "notificationService",
 | 
			
		||||
                    [ "alert" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockAgentService = jasmine.createSpyObj(
 | 
			
		||||
                    "agentService",
 | 
			
		||||
                    [ "isMobile", "isBrowser" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockAgentService.isBrowser.andCallFake(function (substr) {
 | 
			
		||||
                    substr = substr.toLowerCase();
 | 
			
		||||
                    return testAgent.toLowerCase().indexOf(substr) !== -1;
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            [ false, true ].forEach(function (isMobile) {
 | 
			
		||||
                var deviceType = isMobile ? "mobile" : "desktop",
 | 
			
		||||
                    goodBrowser = isMobile ? MOBILE_BROWSER : DESKTOP_BROWSER,
 | 
			
		||||
                    badBrowsers = UNSUPPORTED_BROWSERS.concat([
 | 
			
		||||
                        isMobile ? DESKTOP_BROWSER : MOBILE_BROWSER
 | 
			
		||||
                    ]);
 | 
			
		||||
 | 
			
		||||
                describe("on " + deviceType + " devices", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        mockAgentService.isMobile.andReturn(isMobile);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("is not shown for " + goodBrowser, function () {
 | 
			
		||||
                        instantiateWith(goodBrowser);
 | 
			
		||||
                        expect(mockNotificationService.alert)
 | 
			
		||||
                            .not.toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    badBrowsers.forEach(function (badBrowser) {
 | 
			
		||||
                        it("is shown for " + badBrowser, function () {
 | 
			
		||||
                            instantiateWith(badBrowser);
 | 
			
		||||
                            expect(mockNotificationService.alert)
 | 
			
		||||
                                .toHaveBeenCalled();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -67,21 +67,13 @@ define(
 | 
			
		||||
                mockScope.ngModel = { testField: 12321 };
 | 
			
		||||
                mockScope.field = "testField";
 | 
			
		||||
                mockScope.structure = { format: "someFormat" };
 | 
			
		||||
                mockScope.ngBlur = jasmine.createSpy('blur');
 | 
			
		||||
 | 
			
		||||
                controller = new DateTimeFieldController(
 | 
			
		||||
                    mockScope,
 | 
			
		||||
                    mockFormatService
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates models from user-entered text", function () {
 | 
			
		||||
                var newText = "1977-05-25 17:30:00";
 | 
			
		||||
 | 
			
		||||
                mockScope.textValue = newText;
 | 
			
		||||
                fireWatch("textValue", newText);
 | 
			
		||||
                expect(mockScope.ngModel.testField)
 | 
			
		||||
                    .toEqual(mockFormat.parse(newText));
 | 
			
		||||
                expect(mockScope.textInvalid).toBeFalsy();
 | 
			
		||||
                fireWatch("ngModel[field]", mockScope.ngModel.testField);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates text from model values", function () {
 | 
			
		||||
@@ -91,16 +83,55 @@ define(
 | 
			
		||||
                expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when valid text is entered", function () {
 | 
			
		||||
                var newText;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    newText = "1977-05-25 17:30:00";
 | 
			
		||||
                    mockScope.textValue = newText;
 | 
			
		||||
                    fireWatch("textValue", newText);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates models from user-entered text", function () {
 | 
			
		||||
                    expect(mockScope.ngModel.testField)
 | 
			
		||||
                        .toEqual(mockFormat.parse(newText));
 | 
			
		||||
                    expect(mockScope.textInvalid).toBeFalsy();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not indicate a blur event", function () {
 | 
			
		||||
                    expect(mockScope.ngBlur).not.toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when a date is chosen via the date picker", function () {
 | 
			
		||||
                var newValue;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    newValue = 12345654321;
 | 
			
		||||
                    mockScope.pickerModel.value = newValue;
 | 
			
		||||
                    fireWatch("pickerModel.value", newValue);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates models", function () {
 | 
			
		||||
                    expect(mockScope.ngModel.testField).toEqual(newValue);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("fires a blur event", function () {
 | 
			
		||||
                    expect(mockScope.ngBlur).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes toggle state for date-time picker", function () {
 | 
			
		||||
                expect(mockScope.picker.active).toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when user input is invalid", function () {
 | 
			
		||||
                var newText, oldValue;
 | 
			
		||||
                var newText, oldText, oldValue;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    newText = "Not a date";
 | 
			
		||||
                    oldValue = mockScope.ngModel.testField;
 | 
			
		||||
                    oldText = mockScope.textValue;
 | 
			
		||||
                    mockScope.textValue = newText;
 | 
			
		||||
                    fireWatch("textValue", newText);
 | 
			
		||||
                });
 | 
			
		||||
@@ -116,6 +147,11 @@ define(
 | 
			
		||||
                it("does not modify user input", function () {
 | 
			
		||||
                    expect(mockScope.textValue).toEqual(newText);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("restores valid text values on request", function () {
 | 
			
		||||
                    mockScope.restoreTextValue();
 | 
			
		||||
                    expect(mockScope.textValue).toEqual(oldText);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not modify valid but irregular user input", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,39 @@ define(
 | 
			
		||||
                    .toHaveBeenCalledWith("ngModel", jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when changes are made via form entry", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockScope.ngModel = {
 | 
			
		||||
                        outer: { start: DAY * 2, end: DAY * 3 },
 | 
			
		||||
                        inner: { start: DAY * 2.25, end: DAY * 2.75 }
 | 
			
		||||
                    };
 | 
			
		||||
                    mockScope.formModel = {
 | 
			
		||||
                        start: DAY * 10000,
 | 
			
		||||
                        end: DAY * 11000
 | 
			
		||||
                    };
 | 
			
		||||
                    // These watches may not exist, but Angular would fire
 | 
			
		||||
                    // them if they did.
 | 
			
		||||
                    fireWatchCollection("formModel", mockScope.formModel);
 | 
			
		||||
                    fireWatch("formModel.start", mockScope.formModel.start);
 | 
			
		||||
                    fireWatch("formModel.end", mockScope.formModel.end);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not immediately make changes to the model", function () {
 | 
			
		||||
                    expect(mockScope.ngModel.outer.start)
 | 
			
		||||
                        .not.toEqual(mockScope.formModel.start);
 | 
			
		||||
                    expect(mockScope.ngModel.outer.end)
 | 
			
		||||
                        .not.toEqual(mockScope.formModel.end);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates model bounds on request", function () {
 | 
			
		||||
                    mockScope.updateBoundsFromForm();
 | 
			
		||||
                    expect(mockScope.ngModel.outer.start)
 | 
			
		||||
                        .toEqual(mockScope.formModel.start);
 | 
			
		||||
                    expect(mockScope.ngModel.outer.end)
 | 
			
		||||
                        .toEqual(mockScope.formModel.end);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when dragged", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockScope.ngModel = {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,95 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/directives/MCTSplitPane"],
 | 
			
		||||
    function (MCTSplitPane) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var JQLITE_METHODS = [
 | 
			
		||||
                'on',
 | 
			
		||||
                'addClass',
 | 
			
		||||
                'children',
 | 
			
		||||
                'eq'
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
        describe("The mct-split-pane directive", function () {
 | 
			
		||||
            var mockParse,
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockInterval,
 | 
			
		||||
                mctSplitPane;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockParse = jasmine.createSpy('$parse');
 | 
			
		||||
                mockLog =
 | 
			
		||||
                    jasmine.createSpyObj('$log', ['warn', 'info', 'debug']);
 | 
			
		||||
                mockInterval = jasmine.createSpy('$interval');
 | 
			
		||||
                mockInterval.cancel = jasmine.createSpy('mockCancel');
 | 
			
		||||
                mctSplitPane = new MCTSplitPane(
 | 
			
		||||
                    mockParse,
 | 
			
		||||
                    mockLog,
 | 
			
		||||
                    mockInterval
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is only applicable as an element", function () {
 | 
			
		||||
                expect(mctSplitPane.restrict).toEqual("E");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when its controller is applied", function () {
 | 
			
		||||
                var mockScope,
 | 
			
		||||
                    mockElement,
 | 
			
		||||
                    testAttrs,
 | 
			
		||||
                    mockChildren,
 | 
			
		||||
                    controller;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockScope =
 | 
			
		||||
                        jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']);
 | 
			
		||||
                    mockElement =
 | 
			
		||||
                        jasmine.createSpyObj('element', JQLITE_METHODS);
 | 
			
		||||
                    testAttrs = {};
 | 
			
		||||
                    mockChildren =
 | 
			
		||||
                        jasmine.createSpyObj('children', JQLITE_METHODS);
 | 
			
		||||
 | 
			
		||||
                    mockElement.children.andReturn(mockChildren);
 | 
			
		||||
                    mockChildren.eq.andReturn(mockChildren);
 | 
			
		||||
                    mockChildren[0] = {};
 | 
			
		||||
 | 
			
		||||
                    controller = mctSplitPane.controller[3](
 | 
			
		||||
                        mockScope,
 | 
			
		||||
                        mockElement,
 | 
			
		||||
                        testAttrs
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("sets an interval which does not trigger digests", function () {
 | 
			
		||||
                    expect(mockInterval.mostRecentCall.args[3]).toBe(false);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -19,8 +19,10 @@
 | 
			
		||||
    "directives/MCTPopup",
 | 
			
		||||
    "directives/MCTResize",
 | 
			
		||||
    "directives/MCTScroll",
 | 
			
		||||
    "directives/MCTSplitPane",
 | 
			
		||||
    "services/Popup",
 | 
			
		||||
    "services/PopupService",
 | 
			
		||||
    "services/UrlService",
 | 
			
		||||
    "StyleSheetLoader"
 | 
			
		||||
    "StyleSheetLoader",
 | 
			
		||||
    "UnsupportedBrowserWarning"
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ define(
 | 
			
		||||
            var userAgent = $window.navigator.userAgent,
 | 
			
		||||
                matches = userAgent.match(/iPad|iPhone|Android/i) || [];
 | 
			
		||||
 | 
			
		||||
            this.userAgent = userAgent;
 | 
			
		||||
            this.mobileName = matches[0];
 | 
			
		||||
            this.$window = $window;
 | 
			
		||||
        }
 | 
			
		||||
@@ -91,6 +92,18 @@ define(
 | 
			
		||||
            return !this.isPortrait();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user agent matches a certain named device,
 | 
			
		||||
         * as indicated by checking for a case-insensitive substring
 | 
			
		||||
         * match.
 | 
			
		||||
         * @param {string} name the name to check for
 | 
			
		||||
         * @returns {boolean} true if the user agent includes that name
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isBrowser = function (name) {
 | 
			
		||||
            name = name.toLowerCase();
 | 
			
		||||
            return this.userAgent.toLowerCase().indexOf(name) !== -1;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return AgentService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -81,6 +81,13 @@ define(
 | 
			
		||||
                expect(agentService.isPortrait()).toBeTruthy();
 | 
			
		||||
                expect(agentService.isLandscape()).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows for checking browser type", function () {
 | 
			
		||||
                testWindow.navigator.userAgent = "Chromezilla Safarifox";
 | 
			
		||||
                agentService = new AgentService(testWindow);
 | 
			
		||||
                expect(agentService.isBrowser("Chrome")).toBe(true);
 | 
			
		||||
                expect(agentService.isBrowser("Firefox")).toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -173,7 +173,7 @@ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu,
 | 
			
		||||
/* REQUIRES /platform/commonUI/general/res/sass/_constants.scss */
 | 
			
		||||
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
 | 
			
		||||
/************************** MOBILE TREE MENU DIMENSIONS */
 | 
			
		||||
/************************** WINDOW DIMENSIONS FOR RWD */
 | 
			
		||||
/************************** DEVICE WIDTHS */
 | 
			
		||||
/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
 | 
			
		||||
/************************** MEDIA QUERIES: WINDOWS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
 | 
			
		||||
/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */
 | 
			
		||||
@@ -990,7 +990,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 76, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
    .l-infobubble-wrapper.arw-left .l-infobubble::before {
 | 
			
		||||
      right: 100%; }
 | 
			
		||||
      @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
      @media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
        /* line 76, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
        .l-infobubble-wrapper.arw-left .l-infobubble::before {
 | 
			
		||||
          width: 0;
 | 
			
		||||
@@ -998,14 +998,14 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
          border-top: 6.66667px solid transparent;
 | 
			
		||||
          border-bottom: 6.66667px solid transparent;
 | 
			
		||||
          border-right: 10px solid #ddd; } }
 | 
			
		||||
  @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 88, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
    .l-infobubble-wrapper.arw-right {
 | 
			
		||||
      margin-right: 20px; } }
 | 
			
		||||
  /* line 95, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
  .l-infobubble-wrapper.arw-right .l-infobubble::before {
 | 
			
		||||
    left: 100%; }
 | 
			
		||||
    @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 95, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
      .l-infobubble-wrapper.arw-right .l-infobubble::before {
 | 
			
		||||
        width: 0;
 | 
			
		||||
@@ -1647,7 +1647,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .s-btn.major .icon, .major.s-menu-btn .icon, .s-btn.major .t-item-icon, .major.s-menu-btn .t-item-icon {
 | 
			
		||||
      color: #fff; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover {
 | 
			
		||||
        background: linear-gradient(#1ac6ff, #00bfff); }
 | 
			
		||||
@@ -1686,7 +1686,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon, .s-btn:not(.major) .t-item-icon, .s-menu-btn:not(.major) .t-item-icon {
 | 
			
		||||
      color: #0099cc; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover {
 | 
			
		||||
        background: linear-gradient(#6b6b6b, #5e5e5e); }
 | 
			
		||||
@@ -1728,7 +1728,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon, .s-btn.pause-play.paused .t-item-icon, .pause-play.paused.s-menu-btn .t-item-icon {
 | 
			
		||||
      color: #fff; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover {
 | 
			
		||||
        background: linear-gradient(#fe9815, #f88c01); }
 | 
			
		||||
@@ -1759,7 +1759,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
  .s-btn.show-thumbs .icon:before, .show-thumbs.s-menu-btn .icon:before, .s-btn.show-thumbs .t-item-icon:before, .show-thumbs.s-menu-btn .t-item-icon:before {
 | 
			
		||||
    content: "\000039"; }
 | 
			
		||||
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 101, ../../../../general/res/sass/controls/_buttons.scss */
 | 
			
		||||
  .mini-tab {
 | 
			
		||||
    -moz-border-radius: 3px;
 | 
			
		||||
@@ -1829,14 +1829,14 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
      /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .mini-tab.collapsed .icon, .mini-tab.collapsed .t-item-icon {
 | 
			
		||||
        color: #0099cc; } }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px) and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 800px) and (min-device-height: 1025px) and (min-device-width: 1025px) and (min-device-height: 800px), screen and (min-device-width: 1025px) and (min-device-height: 800px) and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .mini-tab.collapsed:not(.disabled):hover {
 | 
			
		||||
        background: linear-gradient(#6b6b6b, #5e5e5e); }
 | 
			
		||||
        /* line 304, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
        .mini-tab.collapsed:not(.disabled):hover > .icon, .mini-tab.collapsed:not(.disabled):hover > .t-item-icon {
 | 
			
		||||
          color: #33ccff; } }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 138, ../../../../general/res/sass/controls/_buttons.scss */
 | 
			
		||||
      .mini-tab.collapsed:before {
 | 
			
		||||
        opacity: 0; }
 | 
			
		||||
@@ -1925,7 +1925,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
        .mini-tab.anchor-right.collapsed:hover:before {
 | 
			
		||||
          right: 2px; } }
 | 
			
		||||
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 208, ../../../../general/res/sass/controls/_buttons.scss */
 | 
			
		||||
  .mini-tab-icon {
 | 
			
		||||
    color: #595959;
 | 
			
		||||
@@ -2314,7 +2314,7 @@ label.checkbox.custom {
 | 
			
		||||
    font-size: 0.7em;
 | 
			
		||||
    flex: 0 0 1;
 | 
			
		||||
    -webkit-flex: 0 0 1; }
 | 
			
		||||
  @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 240, ../../../../general/res/sass/controls/_controls.scss */
 | 
			
		||||
    .object-header .context-available {
 | 
			
		||||
      -moz-transition-property: opacity;
 | 
			
		||||
@@ -2657,7 +2657,7 @@ label.checkbox.custom {
 | 
			
		||||
        color: inherit; }
 | 
			
		||||
 | 
			
		||||
/******************************************************** BROWSER ELEMENTS */
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 485, ../../../../general/res/sass/controls/_controls.scss */
 | 
			
		||||
  ::-webkit-scrollbar {
 | 
			
		||||
    -moz-border-radius: 2px;
 | 
			
		||||
@@ -3359,7 +3359,7 @@ label.checkbox.custom {
 | 
			
		||||
  /* line 213, ../../../../general/res/sass/controls/_messages.scss */
 | 
			
		||||
  .t-message-single .message-severity-error .type-icon.message-type:before {
 | 
			
		||||
    content: "\21"; }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 259, ../../../../general/res/sass/controls/_messages.scss */
 | 
			
		||||
  .t-message-single .l-message,
 | 
			
		||||
  .t-message-single .bottom-bar {
 | 
			
		||||
@@ -3430,7 +3430,7 @@ label.checkbox.custom {
 | 
			
		||||
  .t-message-list .message-contents .l-message .top-bar,
 | 
			
		||||
  .t-message-list .message-contents .l-message .message-body {
 | 
			
		||||
    margin-bottom: 10px; }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 304, ../../../../general/res/sass/controls/_messages.scss */
 | 
			
		||||
  .t-message-list .message-contents .l-message {
 | 
			
		||||
    margin-right: 10px; } }
 | 
			
		||||
@@ -3682,7 +3682,7 @@ mct-include.l-time-controller {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 25, ../../../../general/res/sass/mobile/controls/_menus.scss */
 | 
			
		||||
  .super-menu {
 | 
			
		||||
    width: 250px;
 | 
			
		||||
@@ -3956,20 +3956,19 @@ textarea {
 | 
			
		||||
  text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px;
 | 
			
		||||
  margin: 0 0 2px 0;
 | 
			
		||||
  padding: 0 5px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  line-height: 22px; }
 | 
			
		||||
  /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
  .select .icon, .select .t-item-icon {
 | 
			
		||||
    color: #0099cc; }
 | 
			
		||||
  @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .select:not(.disabled):hover {
 | 
			
		||||
      background: linear-gradient(#6b6b6b, #5e5e5e); }
 | 
			
		||||
      /* line 304, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .select:not(.disabled):hover > .icon, .select:not(.disabled):hover > .t-item-icon {
 | 
			
		||||
        color: #33ccff; } }
 | 
			
		||||
  /* line 31, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  /* line 30, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  .select select {
 | 
			
		||||
    -moz-appearance: none;
 | 
			
		||||
    -webkit-appearance: none;
 | 
			
		||||
@@ -3982,10 +3981,10 @@ textarea {
 | 
			
		||||
    border: none !important;
 | 
			
		||||
    padding: 4px 25px 2px 0px;
 | 
			
		||||
    width: 120%; }
 | 
			
		||||
    /* line 40, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
    /* line 39, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
    .select select option {
 | 
			
		||||
      margin: 5px 0; }
 | 
			
		||||
  /* line 44, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  /* line 43, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  .select:after {
 | 
			
		||||
    text-shadow: none;
 | 
			
		||||
    content: '\76';
 | 
			
		||||
@@ -4473,7 +4472,7 @@ span.req {
 | 
			
		||||
  /* line 138, ../../../../general/res/sass/user-environ/_layout.scss */
 | 
			
		||||
  .pane .mini-tab-icon.toggle-pane {
 | 
			
		||||
    z-index: 5; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 138, ../../../../general/res/sass/user-environ/_layout.scss */
 | 
			
		||||
      .pane .mini-tab-icon.toggle-pane {
 | 
			
		||||
        top: 10px;
 | 
			
		||||
@@ -4699,7 +4698,7 @@ span.req {
 | 
			
		||||
.pane-inspect-hidden .l-object-and-inspector .splitter-inspect {
 | 
			
		||||
  opacity: 0; }
 | 
			
		||||
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 312, ../../../../general/res/sass/user-environ/_layout.scss */
 | 
			
		||||
  .holder-all {
 | 
			
		||||
    min-width: 600px; }
 | 
			
		||||
@@ -4771,7 +4770,7 @@ span.req {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 26, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
  .browse-wrapper,
 | 
			
		||||
  .pane {
 | 
			
		||||
@@ -4947,7 +4946,7 @@ span.req {
 | 
			
		||||
    -webkit-transition-delay: 0;
 | 
			
		||||
    transition-delay: 0;
 | 
			
		||||
    opacity: 1; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 146, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
  .pane-tree-showing .pane.left.treeview {
 | 
			
		||||
    width: 90% !important; }
 | 
			
		||||
@@ -4961,7 +4960,7 @@ span.req {
 | 
			
		||||
    /* line 152, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
    .pane-tree-showing .pane.right.items .holder-object-and-inspector {
 | 
			
		||||
      opacity: 0; } }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 160, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
  .desktop-hide {
 | 
			
		||||
    display: none; } }
 | 
			
		||||
@@ -5276,7 +5275,7 @@ span.req {
 | 
			
		||||
    margin-left: 50%;
 | 
			
		||||
    white-space: nowrap; }
 | 
			
		||||
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 5, ../../../../general/res/sass/mobile/search/_search.scss */
 | 
			
		||||
  .search .search-bar .menu-icon {
 | 
			
		||||
    display: none; }
 | 
			
		||||
@@ -5453,7 +5452,7 @@ span.req {
 | 
			
		||||
        /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
        .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon, .overlay .bottom-bar .s-btn:not(.major) .t-item-icon, .overlay .bottom-bar .s-menu-btn:not(.major) .t-item-icon {
 | 
			
		||||
          color: #fff; }
 | 
			
		||||
        @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
        @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
          /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
          .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover {
 | 
			
		||||
            background: linear-gradient(#a6a6a6, #999999); }
 | 
			
		||||
@@ -5486,7 +5485,7 @@ span.req {
 | 
			
		||||
  min-height: 225px;
 | 
			
		||||
  height: 225px; }
 | 
			
		||||
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
  .overlay .clk-icon.close {
 | 
			
		||||
    top: 20px;
 | 
			
		||||
@@ -5504,7 +5503,7 @@ span.req {
 | 
			
		||||
      /* line 17, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
      .overlay > .holder > .contents .top-bar > .title {
 | 
			
		||||
        margin-right: 1.2em; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 27, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
  .overlay > .holder {
 | 
			
		||||
    -moz-border-radius: 0;
 | 
			
		||||
@@ -5575,7 +5574,7 @@ span.req {
 | 
			
		||||
  .t-dialog-sm .overlay > .holder {
 | 
			
		||||
    height: auto;
 | 
			
		||||
    max-height: 100%; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 77, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
  .overlay > .holder .contents .bottom-bar {
 | 
			
		||||
    text-align: center; } }
 | 
			
		||||
@@ -5648,7 +5647,7 @@ ul.tree {
 | 
			
		||||
    margin-left: 5px;
 | 
			
		||||
    font-size: 0.75em;
 | 
			
		||||
    width: 10px; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 56, ../../../../general/res/sass/tree/_tree.scss */
 | 
			
		||||
      .tree-item .view-control:hover,
 | 
			
		||||
      .search-result-item .view-control:hover {
 | 
			
		||||
@@ -5781,7 +5780,7 @@ ul.tree {
 | 
			
		||||
    .tree-item.selected .t-object-label .t-item-icon,
 | 
			
		||||
    .search-result-item.selected .t-object-label .t-item-icon {
 | 
			
		||||
      color: #cccccc; }
 | 
			
		||||
  @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 136, ../../../../general/res/sass/tree/_tree.scss */
 | 
			
		||||
    .tree-item:not(.selected):hover,
 | 
			
		||||
    .search-result-item:not(.selected):hover {
 | 
			
		||||
@@ -5833,7 +5832,7 @@ ul.tree {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 27, ../../../../general/res/sass/mobile/_tree.scss */
 | 
			
		||||
  ul.tree ul.tree {
 | 
			
		||||
    margin-left: 20px; }
 | 
			
		||||
@@ -5924,7 +5923,7 @@ ul.tree {
 | 
			
		||||
/* line 65, ../../../../general/res/sass/user-environ/_frame.scss */
 | 
			
		||||
.frame.frame-template .view-switcher {
 | 
			
		||||
  z-index: 10; }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 71, ../../../../general/res/sass/user-environ/_frame.scss */
 | 
			
		||||
  .frame.frame-template .view-switcher {
 | 
			
		||||
    opacity: 0; }
 | 
			
		||||
@@ -6426,7 +6425,7 @@ table {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 31, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 29, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot {
 | 
			
		||||
  color: #999;
 | 
			
		||||
  font-size: 0.7rem;
 | 
			
		||||
@@ -6434,10 +6433,10 @@ table {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  /****************************** Limits and Out-of-Bounds data */ }
 | 
			
		||||
  /* line 38, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 36, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-axis-area {
 | 
			
		||||
    position: absolute; }
 | 
			
		||||
    /* line 41, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 38, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-axis-area.gl-plot-x {
 | 
			
		||||
      top: auto;
 | 
			
		||||
      right: 0;
 | 
			
		||||
@@ -6446,14 +6445,14 @@ table {
 | 
			
		||||
      height: 32px;
 | 
			
		||||
      width: auto;
 | 
			
		||||
      overflow: hidden; }
 | 
			
		||||
    /* line 50, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 47, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-axis-area.gl-plot-y {
 | 
			
		||||
      top: 25px;
 | 
			
		||||
      right: auto;
 | 
			
		||||
      bottom: 37px;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      width: 60px; }
 | 
			
		||||
  /* line 59, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 56, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-coords {
 | 
			
		||||
    -moz-box-sizing: border-box;
 | 
			
		||||
    -webkit-box-sizing: border-box;
 | 
			
		||||
@@ -6470,10 +6469,10 @@ table {
 | 
			
		||||
    bottom: auto;
 | 
			
		||||
    left: 70px;
 | 
			
		||||
    z-index: 10; }
 | 
			
		||||
    /* line 71, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 68, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-coords:empty {
 | 
			
		||||
      display: none; }
 | 
			
		||||
  /* line 76, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 73, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-display-area {
 | 
			
		||||
    background-color: rgba(0, 0, 0, 0.1);
 | 
			
		||||
    position: absolute;
 | 
			
		||||
@@ -6483,13 +6482,13 @@ table {
 | 
			
		||||
    left: 60px;
 | 
			
		||||
    cursor: crosshair;
 | 
			
		||||
    border: 1px solid rgba(153, 153, 153, 0.1); }
 | 
			
		||||
  /* line 89, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 86, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-label,
 | 
			
		||||
  .gl-plot .l-plot-label {
 | 
			
		||||
    color: #666666;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    text-align: center; }
 | 
			
		||||
    /* line 97, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 92, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-label.gl-plot-x-label, .gl-plot .gl-plot-label.l-plot-x-label,
 | 
			
		||||
    .gl-plot .l-plot-label.gl-plot-x-label,
 | 
			
		||||
    .gl-plot .l-plot-label.l-plot-x-label {
 | 
			
		||||
@@ -6498,7 +6497,7 @@ table {
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      height: auto; }
 | 
			
		||||
    /* line 106, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 101, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-label.gl-plot-y-label, .gl-plot .gl-plot-label.l-plot-y-label,
 | 
			
		||||
    .gl-plot .l-plot-label.gl-plot-y-label,
 | 
			
		||||
    .gl-plot .l-plot-label.l-plot-y-label {
 | 
			
		||||
@@ -6515,30 +6514,38 @@ table {
 | 
			
		||||
      left: 0;
 | 
			
		||||
      top: 50%;
 | 
			
		||||
      white-space: nowrap; }
 | 
			
		||||
  /* line 120, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 115, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-x-options,
 | 
			
		||||
  .gl-plot .gl-plot-y-options {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    right: auto;
 | 
			
		||||
    bottom: auto;
 | 
			
		||||
    left: auto5px;
 | 
			
		||||
    margin-top: -16px;
 | 
			
		||||
    height: auto;
 | 
			
		||||
    min-height: 32px;
 | 
			
		||||
    width: 32px; }
 | 
			
		||||
  /* line 134, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    z-index: 2; }
 | 
			
		||||
  /* line 124, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-x-options {
 | 
			
		||||
    top: 5px; }
 | 
			
		||||
  /* line 128, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-y-options {
 | 
			
		||||
    -moz-transform: translateY(-50%);
 | 
			
		||||
    -ms-transform: translateY(-50%);
 | 
			
		||||
    -webkit-transform: translateY(-50%);
 | 
			
		||||
    transform: translateY(-50%);
 | 
			
		||||
    min-width: 150px;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    left: 20px; }
 | 
			
		||||
  /* line 135, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-hash {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    border: 0 rgba(255, 255, 255, 0.2) dashed; }
 | 
			
		||||
    /* line 137, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 138, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-hash.hash-v {
 | 
			
		||||
      border-right-width: 1px;
 | 
			
		||||
      height: 100%; }
 | 
			
		||||
    /* line 141, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 142, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-hash.hash-h {
 | 
			
		||||
      border-bottom-width: 1px;
 | 
			
		||||
      width: 100%; }
 | 
			
		||||
  /* line 147, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 148, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-legend {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
@@ -6548,24 +6555,24 @@ table {
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    overflow-y: auto; }
 | 
			
		||||
  /* line 160, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 161, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .l-limit-bar,
 | 
			
		||||
  .gl-plot .l-oob-data {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    width: auto; }
 | 
			
		||||
  /* line 168, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 169, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .l-limit-bar {
 | 
			
		||||
    height: auto;
 | 
			
		||||
    z-index: 0; }
 | 
			
		||||
    /* line 176, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 177, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-limit-bar.s-limit-yellow {
 | 
			
		||||
      background: rgba(255, 170, 0, 0.2); }
 | 
			
		||||
    /* line 177, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 178, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-limit-bar.s-limit-red {
 | 
			
		||||
      background: rgba(255, 0, 0, 0.2); }
 | 
			
		||||
  /* line 180, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 181, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .l-oob-data {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
@@ -6578,7 +6585,7 @@ table {
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
    height: 10px;
 | 
			
		||||
    z-index: 1; }
 | 
			
		||||
    /* line 188, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 189, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-oob-data.l-oob-data-up {
 | 
			
		||||
      top: 0;
 | 
			
		||||
      bottom: auto;
 | 
			
		||||
@@ -6587,7 +6594,7 @@ table {
 | 
			
		||||
      background-image: -moz-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
 | 
			
		||||
      background-image: -webkit-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
 | 
			
		||||
      background-image: linear-gradient(0deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); }
 | 
			
		||||
    /* line 193, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 194, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-oob-data.l-oob-data-dwn {
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      top: auto;
 | 
			
		||||
@@ -6597,7 +6604,7 @@ table {
 | 
			
		||||
      background-image: -webkit-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
 | 
			
		||||
      background-image: linear-gradient(180deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); }
 | 
			
		||||
 | 
			
		||||
/* line 203, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 204, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-legend .plot-legend-item,
 | 
			
		||||
.gl-plot-legend .legend-item,
 | 
			
		||||
.legend .plot-legend-item,
 | 
			
		||||
@@ -6605,13 +6612,13 @@ table {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin-right: 10px;
 | 
			
		||||
  margin-bottom: 3px; }
 | 
			
		||||
  /* line 208, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 209, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-legend .plot-legend-item span,
 | 
			
		||||
  .gl-plot-legend .legend-item span,
 | 
			
		||||
  .legend .plot-legend-item span,
 | 
			
		||||
  .legend .legend-item span {
 | 
			
		||||
    vertical-align: middle; }
 | 
			
		||||
  /* line 211, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 212, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-legend .plot-legend-item .plot-color-swatch,
 | 
			
		||||
  .gl-plot-legend .plot-legend-item .color-swatch,
 | 
			
		||||
  .gl-plot-legend .legend-item .plot-color-swatch,
 | 
			
		||||
@@ -6627,29 +6634,29 @@ table {
 | 
			
		||||
    height: 8px;
 | 
			
		||||
    width: 8px; }
 | 
			
		||||
 | 
			
		||||
/* line 228, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 223, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-legend .plot-legend-item {
 | 
			
		||||
  -moz-border-radius: 2px;
 | 
			
		||||
  -webkit-border-radius: 2px;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  line-height: 1.5em;
 | 
			
		||||
  padding: 0px 5px; }
 | 
			
		||||
  /* line 234, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 227, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-legend .plot-legend-item .plot-color-swatch {
 | 
			
		||||
    border: 1px solid #333;
 | 
			
		||||
    height: 9px;
 | 
			
		||||
    width: 9px; }
 | 
			
		||||
 | 
			
		||||
/* line 242, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 235, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.tick {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  border: 0 rgba(255, 255, 255, 0.2) solid; }
 | 
			
		||||
  /* line 245, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 238, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .tick.tick-x {
 | 
			
		||||
    border-right-width: 1px;
 | 
			
		||||
    height: 100%; }
 | 
			
		||||
 | 
			
		||||
/* line 251, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 244, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-tick,
 | 
			
		||||
.tick-label {
 | 
			
		||||
  font-size: 0.7rem;
 | 
			
		||||
@@ -6657,7 +6664,7 @@ table {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  text-overflow: ellipsis; }
 | 
			
		||||
  /* line 259, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 251, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-tick.gl-plot-x-tick-label, .gl-plot-tick.tick-label-x,
 | 
			
		||||
  .tick-label.gl-plot-x-tick-label,
 | 
			
		||||
  .tick-label.tick-label-x {
 | 
			
		||||
@@ -6668,7 +6675,7 @@ table {
 | 
			
		||||
    width: 20%;
 | 
			
		||||
    margin-left: -10%;
 | 
			
		||||
    text-align: center; }
 | 
			
		||||
  /* line 269, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 261, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-tick.gl-plot-y-tick-label, .gl-plot-tick.tick-label-y,
 | 
			
		||||
  .tick-label.gl-plot-y-tick-label,
 | 
			
		||||
  .tick-label.tick-label-y {
 | 
			
		||||
@@ -6678,18 +6685,18 @@ table {
 | 
			
		||||
    margin-bottom: -0.5em;
 | 
			
		||||
    text-align: right; }
 | 
			
		||||
 | 
			
		||||
/* line 281, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 272, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-tick.gl-plot-x-tick-label {
 | 
			
		||||
  top: 5px; }
 | 
			
		||||
/* line 284, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 275, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-tick.gl-plot-y-tick-label {
 | 
			
		||||
  right: 5px;
 | 
			
		||||
  left: 5px; }
 | 
			
		||||
 | 
			
		||||
/* line 291, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 282, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.tick-label.tick-label-x {
 | 
			
		||||
  top: 0; }
 | 
			
		||||
/* line 294, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 285, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.tick-label.tick-label-y {
 | 
			
		||||
  right: 0;
 | 
			
		||||
  left: 0; }
 | 
			
		||||
@@ -6816,7 +6823,7 @@ table {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .items-holder .item.grid-item .icon, .items-holder .item.grid-item .t-item-icon {
 | 
			
		||||
      color: #0099cc; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .items-holder .item.grid-item:not(.disabled):hover {
 | 
			
		||||
        background: linear-gradient(#666666, #595959); }
 | 
			
		||||
@@ -6947,7 +6954,7 @@ table {
 | 
			
		||||
      /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .items-holder .item.grid-item.selected .icon, .items-holder .item.grid-item.selected .t-item-icon {
 | 
			
		||||
        color: #0099cc; }
 | 
			
		||||
      @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
      @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
        /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
        .items-holder .item.grid-item.selected:not(.disabled):hover {
 | 
			
		||||
          background: linear-gradient(#1ac6ff, #00bfff); }
 | 
			
		||||
@@ -6988,7 +6995,7 @@ table {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 29, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
  .items-holder .item.grid-item {
 | 
			
		||||
    width: 100%; }
 | 
			
		||||
@@ -7022,7 +7029,7 @@ table {
 | 
			
		||||
      opacity: 1;
 | 
			
		||||
      font-size: 1em;
 | 
			
		||||
      width: auto; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 29, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
  .items-holder .item.grid-item {
 | 
			
		||||
    height: 50px; }
 | 
			
		||||
@@ -7042,7 +7049,7 @@ table {
 | 
			
		||||
    /* line 83, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
    .items-holder .item.grid-item .item-main .item-open {
 | 
			
		||||
      line-height: 50px; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 29, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
  .items-holder .item.grid-item {
 | 
			
		||||
    height: 66px; }
 | 
			
		||||
 
 | 
			
		||||
@@ -173,7 +173,7 @@ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu,
 | 
			
		||||
/* REQUIRES /platform/commonUI/general/res/sass/_constants.scss */
 | 
			
		||||
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
 | 
			
		||||
/************************** MOBILE TREE MENU DIMENSIONS */
 | 
			
		||||
/************************** WINDOW DIMENSIONS FOR RWD */
 | 
			
		||||
/************************** DEVICE WIDTHS */
 | 
			
		||||
/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
 | 
			
		||||
/************************** MEDIA QUERIES: WINDOWS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
 | 
			
		||||
/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */
 | 
			
		||||
@@ -990,7 +990,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 76, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
    .l-infobubble-wrapper.arw-left .l-infobubble::before {
 | 
			
		||||
      right: 100%; }
 | 
			
		||||
      @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
      @media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
        /* line 76, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
        .l-infobubble-wrapper.arw-left .l-infobubble::before {
 | 
			
		||||
          width: 0;
 | 
			
		||||
@@ -998,14 +998,14 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
          border-top: 6.66667px solid transparent;
 | 
			
		||||
          border-bottom: 6.66667px solid transparent;
 | 
			
		||||
          border-right: 10px solid white; } }
 | 
			
		||||
  @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 88, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
    .l-infobubble-wrapper.arw-right {
 | 
			
		||||
      margin-right: 20px; } }
 | 
			
		||||
  /* line 95, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
  .l-infobubble-wrapper.arw-right .l-infobubble::before {
 | 
			
		||||
    left: 100%; }
 | 
			
		||||
    @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 95, ../../../../general/res/sass/helpers/_bubbles.scss */
 | 
			
		||||
      .l-infobubble-wrapper.arw-right .l-infobubble::before {
 | 
			
		||||
        width: 0;
 | 
			
		||||
@@ -1619,7 +1619,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .s-btn.major .icon, .major.s-menu-btn .icon, .s-btn.major .t-item-icon, .major.s-menu-btn .t-item-icon {
 | 
			
		||||
      color: #fff; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover {
 | 
			
		||||
        background: deepskyblue; }
 | 
			
		||||
@@ -1649,7 +1649,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon, .s-btn:not(.major) .t-item-icon, .s-menu-btn:not(.major) .t-item-icon {
 | 
			
		||||
      color: #eee; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover {
 | 
			
		||||
        background: #0099cc; }
 | 
			
		||||
@@ -1682,7 +1682,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon, .s-btn.pause-play.paused .t-item-icon, .pause-play.paused.s-menu-btn .t-item-icon {
 | 
			
		||||
      color: #fff; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover {
 | 
			
		||||
        background: #ffad33; }
 | 
			
		||||
@@ -1713,7 +1713,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
  .s-btn.show-thumbs .icon:before, .show-thumbs.s-menu-btn .icon:before, .s-btn.show-thumbs .t-item-icon:before, .show-thumbs.s-menu-btn .t-item-icon:before {
 | 
			
		||||
    content: "\000039"; }
 | 
			
		||||
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 101, ../../../../general/res/sass/controls/_buttons.scss */
 | 
			
		||||
  .mini-tab {
 | 
			
		||||
    -moz-border-radius: 4px;
 | 
			
		||||
@@ -1774,14 +1774,14 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
      /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .mini-tab.collapsed .icon, .mini-tab.collapsed .t-item-icon {
 | 
			
		||||
        color: #eee; } }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px) and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 800px) and (min-device-height: 1025px) and (min-device-width: 1025px) and (min-device-height: 800px), screen and (min-device-width: 1025px) and (min-device-height: 800px) and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .mini-tab.collapsed:not(.disabled):hover {
 | 
			
		||||
        background: #0099cc; }
 | 
			
		||||
        /* line 304, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
        .mini-tab.collapsed:not(.disabled):hover > .icon, .mini-tab.collapsed:not(.disabled):hover > .t-item-icon {
 | 
			
		||||
          color: white; } }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 138, ../../../../general/res/sass/controls/_buttons.scss */
 | 
			
		||||
      .mini-tab.collapsed:before {
 | 
			
		||||
        opacity: 0; }
 | 
			
		||||
@@ -1870,7 +1870,7 @@ tr[class*="s-limit"].s-limit-lwr td:first-child:before {
 | 
			
		||||
        .mini-tab.anchor-right.collapsed:hover:before {
 | 
			
		||||
          right: 2px; } }
 | 
			
		||||
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 208, ../../../../general/res/sass/controls/_buttons.scss */
 | 
			
		||||
  .mini-tab-icon {
 | 
			
		||||
    color: #d6d6d6;
 | 
			
		||||
@@ -2259,7 +2259,7 @@ label.checkbox.custom {
 | 
			
		||||
    font-size: 0.7em;
 | 
			
		||||
    flex: 0 0 1;
 | 
			
		||||
    -webkit-flex: 0 0 1; }
 | 
			
		||||
  @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 240, ../../../../general/res/sass/controls/_controls.scss */
 | 
			
		||||
    .object-header .context-available {
 | 
			
		||||
      -moz-transition-property: opacity;
 | 
			
		||||
@@ -2602,7 +2602,7 @@ label.checkbox.custom {
 | 
			
		||||
        color: inherit; }
 | 
			
		||||
 | 
			
		||||
/******************************************************** BROWSER ELEMENTS */
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 485, ../../../../general/res/sass/controls/_controls.scss */
 | 
			
		||||
  ::-webkit-scrollbar {
 | 
			
		||||
    -moz-border-radius: 2px;
 | 
			
		||||
@@ -3298,7 +3298,7 @@ label.checkbox.custom {
 | 
			
		||||
  /* line 213, ../../../../general/res/sass/controls/_messages.scss */
 | 
			
		||||
  .t-message-single .message-severity-error .type-icon.message-type:before {
 | 
			
		||||
    content: "\21"; }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 259, ../../../../general/res/sass/controls/_messages.scss */
 | 
			
		||||
  .t-message-single .l-message,
 | 
			
		||||
  .t-message-single .bottom-bar {
 | 
			
		||||
@@ -3369,7 +3369,7 @@ label.checkbox.custom {
 | 
			
		||||
  .t-message-list .message-contents .l-message .top-bar,
 | 
			
		||||
  .t-message-list .message-contents .l-message .message-body {
 | 
			
		||||
    margin-bottom: 10px; }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 304, ../../../../general/res/sass/controls/_messages.scss */
 | 
			
		||||
  .t-message-list .message-contents .l-message {
 | 
			
		||||
    margin-right: 10px; } }
 | 
			
		||||
@@ -3621,7 +3621,7 @@ mct-include.l-time-controller {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 25, ../../../../general/res/sass/mobile/controls/_menus.scss */
 | 
			
		||||
  .super-menu {
 | 
			
		||||
    width: 250px;
 | 
			
		||||
@@ -3885,13 +3885,12 @@ textarea {
 | 
			
		||||
  transition: background, 0.25s;
 | 
			
		||||
  text-shadow: none;
 | 
			
		||||
  padding: 0 5px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  line-height: 22px; }
 | 
			
		||||
  /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
  .select .icon, .select .t-item-icon {
 | 
			
		||||
    color: #eee; }
 | 
			
		||||
  /* line 31, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  /* line 30, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  .select select {
 | 
			
		||||
    -moz-appearance: none;
 | 
			
		||||
    -webkit-appearance: none;
 | 
			
		||||
@@ -3904,10 +3903,10 @@ textarea {
 | 
			
		||||
    border: none !important;
 | 
			
		||||
    padding: 4px 25px 2px 0px;
 | 
			
		||||
    width: 120%; }
 | 
			
		||||
    /* line 40, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
    /* line 39, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
    .select select option {
 | 
			
		||||
      margin: 5px 0; }
 | 
			
		||||
  /* line 44, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  /* line 43, ../../../../general/res/sass/forms/_selects.scss */
 | 
			
		||||
  .select:after {
 | 
			
		||||
    text-shadow: none;
 | 
			
		||||
    content: '\76';
 | 
			
		||||
@@ -4395,7 +4394,7 @@ span.req {
 | 
			
		||||
  /* line 138, ../../../../general/res/sass/user-environ/_layout.scss */
 | 
			
		||||
  .pane .mini-tab-icon.toggle-pane {
 | 
			
		||||
    z-index: 5; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 138, ../../../../general/res/sass/user-environ/_layout.scss */
 | 
			
		||||
      .pane .mini-tab-icon.toggle-pane {
 | 
			
		||||
        top: 10px;
 | 
			
		||||
@@ -4621,7 +4620,7 @@ span.req {
 | 
			
		||||
.pane-inspect-hidden .l-object-and-inspector .splitter-inspect {
 | 
			
		||||
  opacity: 0; }
 | 
			
		||||
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 312, ../../../../general/res/sass/user-environ/_layout.scss */
 | 
			
		||||
  .holder-all {
 | 
			
		||||
    min-width: 600px; }
 | 
			
		||||
@@ -4693,7 +4692,7 @@ span.req {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 26, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
  .browse-wrapper,
 | 
			
		||||
  .pane {
 | 
			
		||||
@@ -4869,7 +4868,7 @@ span.req {
 | 
			
		||||
    -webkit-transition-delay: 0;
 | 
			
		||||
    transition-delay: 0;
 | 
			
		||||
    opacity: 1; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 146, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
  .pane-tree-showing .pane.left.treeview {
 | 
			
		||||
    width: 90% !important; }
 | 
			
		||||
@@ -4883,7 +4882,7 @@ span.req {
 | 
			
		||||
    /* line 152, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
    .pane-tree-showing .pane.right.items .holder-object-and-inspector {
 | 
			
		||||
      opacity: 0; } }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 160, ../../../../general/res/sass/mobile/_layout.scss */
 | 
			
		||||
  .desktop-hide {
 | 
			
		||||
    display: none; } }
 | 
			
		||||
@@ -5198,7 +5197,7 @@ span.req {
 | 
			
		||||
    margin-left: 50%;
 | 
			
		||||
    white-space: nowrap; }
 | 
			
		||||
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 5, ../../../../general/res/sass/mobile/search/_search.scss */
 | 
			
		||||
  .search .search-bar .menu-icon {
 | 
			
		||||
    display: none; }
 | 
			
		||||
@@ -5357,7 +5356,7 @@ span.req {
 | 
			
		||||
        /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
        .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon, .overlay .bottom-bar .s-btn:not(.major) .t-item-icon, .overlay .bottom-bar .s-menu-btn:not(.major) .t-item-icon {
 | 
			
		||||
          color: #fff; }
 | 
			
		||||
        @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
        @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
          /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
          .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover {
 | 
			
		||||
            background: #7d7d7d; }
 | 
			
		||||
@@ -5390,7 +5389,7 @@ span.req {
 | 
			
		||||
  min-height: 225px;
 | 
			
		||||
  height: 225px; }
 | 
			
		||||
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
  .overlay .clk-icon.close {
 | 
			
		||||
    top: 20px;
 | 
			
		||||
@@ -5408,7 +5407,7 @@ span.req {
 | 
			
		||||
      /* line 17, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
      .overlay > .holder > .contents .top-bar > .title {
 | 
			
		||||
        margin-right: 1.2em; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 27, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
  .overlay > .holder {
 | 
			
		||||
    -moz-border-radius: 0;
 | 
			
		||||
@@ -5479,7 +5478,7 @@ span.req {
 | 
			
		||||
  .t-dialog-sm .overlay > .holder {
 | 
			
		||||
    height: auto;
 | 
			
		||||
    max-height: 100%; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 77, ../../../../general/res/sass/mobile/overlay/_overlay.scss */
 | 
			
		||||
  .overlay > .holder .contents .bottom-bar {
 | 
			
		||||
    text-align: center; } }
 | 
			
		||||
@@ -5552,7 +5551,7 @@ ul.tree {
 | 
			
		||||
    margin-left: 5px;
 | 
			
		||||
    font-size: 0.75em;
 | 
			
		||||
    width: 10px; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 56, ../../../../general/res/sass/tree/_tree.scss */
 | 
			
		||||
      .tree-item .view-control:hover,
 | 
			
		||||
      .search-result-item .view-control:hover {
 | 
			
		||||
@@ -5683,7 +5682,7 @@ ul.tree {
 | 
			
		||||
    .tree-item.selected .t-object-label .t-item-icon,
 | 
			
		||||
    .search-result-item.selected .t-object-label .t-item-icon {
 | 
			
		||||
      color: #fcfcfc; }
 | 
			
		||||
  @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
  @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
    /* line 136, ../../../../general/res/sass/tree/_tree.scss */
 | 
			
		||||
    .tree-item:not(.selected):hover,
 | 
			
		||||
    .search-result-item:not(.selected):hover {
 | 
			
		||||
@@ -5735,7 +5734,7 @@ ul.tree {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 27, ../../../../general/res/sass/mobile/_tree.scss */
 | 
			
		||||
  ul.tree ul.tree {
 | 
			
		||||
    margin-left: 20px; }
 | 
			
		||||
@@ -5826,7 +5825,7 @@ ul.tree {
 | 
			
		||||
/* line 65, ../../../../general/res/sass/user-environ/_frame.scss */
 | 
			
		||||
.frame.frame-template .view-switcher {
 | 
			
		||||
  z-index: 10; }
 | 
			
		||||
@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
@media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
  /* line 71, ../../../../general/res/sass/user-environ/_frame.scss */
 | 
			
		||||
  .frame.frame-template .view-switcher {
 | 
			
		||||
    opacity: 0; }
 | 
			
		||||
@@ -6328,7 +6327,7 @@ table {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 31, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 29, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot {
 | 
			
		||||
  color: #666;
 | 
			
		||||
  font-size: 0.7rem;
 | 
			
		||||
@@ -6336,10 +6335,10 @@ table {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  /****************************** Limits and Out-of-Bounds data */ }
 | 
			
		||||
  /* line 38, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 36, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-axis-area {
 | 
			
		||||
    position: absolute; }
 | 
			
		||||
    /* line 41, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 38, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-axis-area.gl-plot-x {
 | 
			
		||||
      top: auto;
 | 
			
		||||
      right: 0;
 | 
			
		||||
@@ -6348,14 +6347,14 @@ table {
 | 
			
		||||
      height: 32px;
 | 
			
		||||
      width: auto;
 | 
			
		||||
      overflow: hidden; }
 | 
			
		||||
    /* line 50, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 47, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-axis-area.gl-plot-y {
 | 
			
		||||
      top: 25px;
 | 
			
		||||
      right: auto;
 | 
			
		||||
      bottom: 37px;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      width: 60px; }
 | 
			
		||||
  /* line 59, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 56, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-coords {
 | 
			
		||||
    -moz-box-sizing: border-box;
 | 
			
		||||
    -webkit-box-sizing: border-box;
 | 
			
		||||
@@ -6372,10 +6371,10 @@ table {
 | 
			
		||||
    bottom: auto;
 | 
			
		||||
    left: 70px;
 | 
			
		||||
    z-index: 10; }
 | 
			
		||||
    /* line 71, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 68, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-coords:empty {
 | 
			
		||||
      display: none; }
 | 
			
		||||
  /* line 76, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 73, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-display-area {
 | 
			
		||||
    background-color: rgba(0, 0, 0, 0.05);
 | 
			
		||||
    position: absolute;
 | 
			
		||||
@@ -6385,13 +6384,13 @@ table {
 | 
			
		||||
    left: 60px;
 | 
			
		||||
    cursor: crosshair;
 | 
			
		||||
    border: 1px solid rgba(102, 102, 102, 0.2); }
 | 
			
		||||
  /* line 89, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 86, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-label,
 | 
			
		||||
  .gl-plot .l-plot-label {
 | 
			
		||||
    color: #999999;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    text-align: center; }
 | 
			
		||||
    /* line 97, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 92, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-label.gl-plot-x-label, .gl-plot .gl-plot-label.l-plot-x-label,
 | 
			
		||||
    .gl-plot .l-plot-label.gl-plot-x-label,
 | 
			
		||||
    .gl-plot .l-plot-label.l-plot-x-label {
 | 
			
		||||
@@ -6400,7 +6399,7 @@ table {
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      height: auto; }
 | 
			
		||||
    /* line 106, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 101, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-label.gl-plot-y-label, .gl-plot .gl-plot-label.l-plot-y-label,
 | 
			
		||||
    .gl-plot .l-plot-label.gl-plot-y-label,
 | 
			
		||||
    .gl-plot .l-plot-label.l-plot-y-label {
 | 
			
		||||
@@ -6417,30 +6416,38 @@ table {
 | 
			
		||||
      left: 0;
 | 
			
		||||
      top: 50%;
 | 
			
		||||
      white-space: nowrap; }
 | 
			
		||||
  /* line 120, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 115, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-x-options,
 | 
			
		||||
  .gl-plot .gl-plot-y-options {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    right: auto;
 | 
			
		||||
    bottom: auto;
 | 
			
		||||
    left: auto5px;
 | 
			
		||||
    margin-top: -16px;
 | 
			
		||||
    height: auto;
 | 
			
		||||
    min-height: 32px;
 | 
			
		||||
    width: 32px; }
 | 
			
		||||
  /* line 134, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    z-index: 2; }
 | 
			
		||||
  /* line 124, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-x-options {
 | 
			
		||||
    top: 5px; }
 | 
			
		||||
  /* line 128, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-y-options {
 | 
			
		||||
    -moz-transform: translateY(-50%);
 | 
			
		||||
    -ms-transform: translateY(-50%);
 | 
			
		||||
    -webkit-transform: translateY(-50%);
 | 
			
		||||
    transform: translateY(-50%);
 | 
			
		||||
    min-width: 150px;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    left: 20px; }
 | 
			
		||||
  /* line 135, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-hash {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    border: 0 rgba(0, 0, 0, 0.2) dashed; }
 | 
			
		||||
    /* line 137, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 138, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-hash.hash-v {
 | 
			
		||||
      border-right-width: 1px;
 | 
			
		||||
      height: 100%; }
 | 
			
		||||
    /* line 141, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 142, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .gl-plot-hash.hash-h {
 | 
			
		||||
      border-bottom-width: 1px;
 | 
			
		||||
      width: 100%; }
 | 
			
		||||
  /* line 147, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 148, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .gl-plot-legend {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
@@ -6450,24 +6457,24 @@ table {
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    overflow-y: auto; }
 | 
			
		||||
  /* line 160, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 161, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .l-limit-bar,
 | 
			
		||||
  .gl-plot .l-oob-data {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    width: auto; }
 | 
			
		||||
  /* line 168, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 169, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .l-limit-bar {
 | 
			
		||||
    height: auto;
 | 
			
		||||
    z-index: 0; }
 | 
			
		||||
    /* line 176, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 177, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-limit-bar.s-limit-yellow {
 | 
			
		||||
      background: rgba(255, 170, 0, 0.2); }
 | 
			
		||||
    /* line 177, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 178, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-limit-bar.s-limit-red {
 | 
			
		||||
      background: rgba(255, 0, 0, 0.2); }
 | 
			
		||||
  /* line 180, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 181, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot .l-oob-data {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
@@ -6480,7 +6487,7 @@ table {
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
    height: 10px;
 | 
			
		||||
    z-index: 1; }
 | 
			
		||||
    /* line 188, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 189, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-oob-data.l-oob-data-up {
 | 
			
		||||
      top: 0;
 | 
			
		||||
      bottom: auto;
 | 
			
		||||
@@ -6489,7 +6496,7 @@ table {
 | 
			
		||||
      background-image: -moz-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
 | 
			
		||||
      background-image: -webkit-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
 | 
			
		||||
      background-image: linear-gradient(0deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); }
 | 
			
		||||
    /* line 193, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    /* line 194, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
    .gl-plot .l-oob-data.l-oob-data-dwn {
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      top: auto;
 | 
			
		||||
@@ -6499,7 +6506,7 @@ table {
 | 
			
		||||
      background-image: -webkit-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%);
 | 
			
		||||
      background-image: linear-gradient(180deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); }
 | 
			
		||||
 | 
			
		||||
/* line 203, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 204, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-legend .plot-legend-item,
 | 
			
		||||
.gl-plot-legend .legend-item,
 | 
			
		||||
.legend .plot-legend-item,
 | 
			
		||||
@@ -6507,13 +6514,13 @@ table {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin-right: 10px;
 | 
			
		||||
  margin-bottom: 3px; }
 | 
			
		||||
  /* line 208, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 209, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-legend .plot-legend-item span,
 | 
			
		||||
  .gl-plot-legend .legend-item span,
 | 
			
		||||
  .legend .plot-legend-item span,
 | 
			
		||||
  .legend .legend-item span {
 | 
			
		||||
    vertical-align: middle; }
 | 
			
		||||
  /* line 211, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 212, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-legend .plot-legend-item .plot-color-swatch,
 | 
			
		||||
  .gl-plot-legend .plot-legend-item .color-swatch,
 | 
			
		||||
  .gl-plot-legend .legend-item .plot-color-swatch,
 | 
			
		||||
@@ -6529,29 +6536,29 @@ table {
 | 
			
		||||
    height: 8px;
 | 
			
		||||
    width: 8px; }
 | 
			
		||||
 | 
			
		||||
/* line 228, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 223, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-legend .plot-legend-item {
 | 
			
		||||
  -moz-border-radius: 3px;
 | 
			
		||||
  -webkit-border-radius: 3px;
 | 
			
		||||
  border-radius: 3px;
 | 
			
		||||
  line-height: 1.5em;
 | 
			
		||||
  padding: 0px 5px; }
 | 
			
		||||
  /* line 234, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 227, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-legend .plot-legend-item .plot-color-swatch {
 | 
			
		||||
    border: 1px solid #fcfcfc;
 | 
			
		||||
    height: 9px;
 | 
			
		||||
    width: 9px; }
 | 
			
		||||
 | 
			
		||||
/* line 242, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 235, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.tick {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  border: 0 rgba(0, 0, 0, 0.2) solid; }
 | 
			
		||||
  /* line 245, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 238, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .tick.tick-x {
 | 
			
		||||
    border-right-width: 1px;
 | 
			
		||||
    height: 100%; }
 | 
			
		||||
 | 
			
		||||
/* line 251, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 244, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-tick,
 | 
			
		||||
.tick-label {
 | 
			
		||||
  font-size: 0.7rem;
 | 
			
		||||
@@ -6559,7 +6566,7 @@ table {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  text-overflow: ellipsis; }
 | 
			
		||||
  /* line 259, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 251, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-tick.gl-plot-x-tick-label, .gl-plot-tick.tick-label-x,
 | 
			
		||||
  .tick-label.gl-plot-x-tick-label,
 | 
			
		||||
  .tick-label.tick-label-x {
 | 
			
		||||
@@ -6570,7 +6577,7 @@ table {
 | 
			
		||||
    width: 20%;
 | 
			
		||||
    margin-left: -10%;
 | 
			
		||||
    text-align: center; }
 | 
			
		||||
  /* line 269, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  /* line 261, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
  .gl-plot-tick.gl-plot-y-tick-label, .gl-plot-tick.tick-label-y,
 | 
			
		||||
  .tick-label.gl-plot-y-tick-label,
 | 
			
		||||
  .tick-label.tick-label-y {
 | 
			
		||||
@@ -6580,18 +6587,18 @@ table {
 | 
			
		||||
    margin-bottom: -0.5em;
 | 
			
		||||
    text-align: right; }
 | 
			
		||||
 | 
			
		||||
/* line 281, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 272, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-tick.gl-plot-x-tick-label {
 | 
			
		||||
  top: 5px; }
 | 
			
		||||
/* line 284, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 275, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.gl-plot-tick.gl-plot-y-tick-label {
 | 
			
		||||
  right: 5px;
 | 
			
		||||
  left: 5px; }
 | 
			
		||||
 | 
			
		||||
/* line 291, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 282, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.tick-label.tick-label-x {
 | 
			
		||||
  top: 0; }
 | 
			
		||||
/* line 294, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
/* line 285, ../../../../general/res/sass/plots/_plots-main.scss */
 | 
			
		||||
.tick-label.tick-label-y {
 | 
			
		||||
  right: 0;
 | 
			
		||||
  left: 0; }
 | 
			
		||||
@@ -6709,7 +6716,7 @@ table {
 | 
			
		||||
    /* line 297, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
    .items-holder .item.grid-item .icon, .items-holder .item.grid-item .t-item-icon {
 | 
			
		||||
      color: #0099cc; }
 | 
			
		||||
    @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) {
 | 
			
		||||
    @media only screen and (min-device-width: 1025px) and (-webkit-min-device-pixel-ratio: 1) {
 | 
			
		||||
      /* line 302, ../../../../general/res/sass/_mixins.scss */
 | 
			
		||||
      .items-holder .item.grid-item:not(.disabled):hover {
 | 
			
		||||
        background: #d0d0d0; }
 | 
			
		||||
@@ -6865,7 +6872,7 @@ table {
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px), only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 29, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
  .items-holder .item.grid-item {
 | 
			
		||||
    width: 100%; }
 | 
			
		||||
@@ -6899,7 +6906,7 @@ table {
 | 
			
		||||
      opacity: 1;
 | 
			
		||||
      font-size: 1em;
 | 
			
		||||
      width: auto; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (max-device-width: 767px), only screen and (orientation: landscape) and (max-device-width: 767px) {
 | 
			
		||||
  /* line 29, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
  .items-holder .item.grid-item {
 | 
			
		||||
    height: 50px; }
 | 
			
		||||
@@ -6919,7 +6926,7 @@ table {
 | 
			
		||||
    /* line 83, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
    .items-holder .item.grid-item .item-main .item-open {
 | 
			
		||||
      line-height: 50px; } }
 | 
			
		||||
@media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) {
 | 
			
		||||
@media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px) {
 | 
			
		||||
  /* line 29, ../../../../general/res/sass/mobile/_item.scss */
 | 
			
		||||
  .items-holder .item.grid-item {
 | 
			
		||||
    height: 66px; }
 | 
			
		||||
 
 | 
			
		||||
@@ -72,8 +72,7 @@
 | 
			
		||||
                    "persistenceService",
 | 
			
		||||
                    "$q",
 | 
			
		||||
                    "now",
 | 
			
		||||
                    "PERSISTENCE_SPACE",
 | 
			
		||||
                    "ADDITIONAL_PERSISTENCE_SPACES"
 | 
			
		||||
                    "PERSISTENCE_SPACE"
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -115,6 +114,12 @@
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "views/ViewProvider.js",
 | 
			
		||||
                "depends": [ "views[]", "$log" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "provides": "identifierService",
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "identifiers/IdentifierProvider.js",
 | 
			
		||||
                "depends": [ "PERSISTENCE_SPACE" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "types": [
 | 
			
		||||
@@ -183,7 +188,8 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "persistence",
 | 
			
		||||
                "implementation": "capabilities/PersistenceCapability.js",
 | 
			
		||||
                "depends": [ "persistenceService", "PERSISTENCE_SPACE" ]
 | 
			
		||||
                "depends": [ "persistenceService", "identifierService",
 | 
			
		||||
                    "notificationService", "$q" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "metadata",
 | 
			
		||||
@@ -202,7 +208,7 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "instantiation",
 | 
			
		||||
                "implementation": "capabilities/InstantiationCapability.js",
 | 
			
		||||
                "depends": [ "$injector" ]
 | 
			
		||||
                "depends": [ "$injector", "identifierService" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "services": [
 | 
			
		||||
@@ -245,11 +251,6 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "PERSISTENCE_SPACE",
 | 
			
		||||
                "value": "mct"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "ADDITIONAL_PERSISTENCE_SPACES",
 | 
			
		||||
                "value": [],
 | 
			
		||||
                "description": "An array of additional persistence spaces to load models from."
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "licenses": [
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@
 | 
			
		||||
/*global define,Promise*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../objects/DomainObjectImpl', 'uuid'],
 | 
			
		||||
    function (DomainObjectImpl, uuid) {
 | 
			
		||||
    ['../objects/DomainObjectImpl'],
 | 
			
		||||
    function (DomainObjectImpl) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -33,9 +33,12 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/core
 | 
			
		||||
         * @param $injector Angular's `$injector`
 | 
			
		||||
         * @implements {Capability}
 | 
			
		||||
         */
 | 
			
		||||
        function InstantiationCapability($injector) {
 | 
			
		||||
        function InstantiationCapability($injector, identifierService, domainObject) {
 | 
			
		||||
            this.$injector = $injector;
 | 
			
		||||
            this.identifierService = identifierService;
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -45,19 +48,26 @@ define(
 | 
			
		||||
         * have been persisted, nor will it have been added to the
 | 
			
		||||
         * composition of the object which exposed this capability.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {object} the model for the new domain object
 | 
			
		||||
         * @returns {DomainObject} the new domain object
 | 
			
		||||
         */
 | 
			
		||||
        InstantiationCapability.prototype.instantiate = function (model) {
 | 
			
		||||
            var parsedId =
 | 
			
		||||
                    this.identifierService.parse(this.domainObject.getId()),
 | 
			
		||||
                space = parsedId.getDefinedSpace(),
 | 
			
		||||
                id = this.identifierService.generate(space);
 | 
			
		||||
 | 
			
		||||
            // Lazily initialize; instantiate depends on capabilityService,
 | 
			
		||||
            // which depends on all capabilities, including this one.
 | 
			
		||||
            this.instantiateFn = this.instantiateFn ||
 | 
			
		||||
                this.$injector.get("instantiate");
 | 
			
		||||
            return this.instantiateFn(model);
 | 
			
		||||
 | 
			
		||||
            return this.instantiateFn(model, id);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Alias of `create`.
 | 
			
		||||
         * @see {platform/core.CreationCapability#create}
 | 
			
		||||
         * Alias of `instantiate`.
 | 
			
		||||
         * @see {platform/core.CreationCapability#instantiate}
 | 
			
		||||
         */
 | 
			
		||||
        InstantiationCapability.prototype.invoke =
 | 
			
		||||
            InstantiationCapability.prototype.instantiate;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
/*jslint es5: true */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
@@ -44,13 +45,21 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Capability}
 | 
			
		||||
         */
 | 
			
		||||
        function PersistenceCapability(persistenceService, space, domainObject) {
 | 
			
		||||
        function PersistenceCapability(
 | 
			
		||||
            persistenceService,
 | 
			
		||||
            identifierService,
 | 
			
		||||
            notificationService,
 | 
			
		||||
            $q,
 | 
			
		||||
            domainObject
 | 
			
		||||
        ) {
 | 
			
		||||
            // Cache modified timestamp
 | 
			
		||||
            this.modified = domainObject.getModel().modified;
 | 
			
		||||
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.space = space;
 | 
			
		||||
            this.identifierService = identifierService;
 | 
			
		||||
            this.persistenceService = persistenceService;
 | 
			
		||||
            this.notificationService = notificationService;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Utility function for creating promise-like objects which
 | 
			
		||||
@@ -63,6 +72,51 @@ define(
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function getKey(id) {
 | 
			
		||||
            var parts = id.split(":");
 | 
			
		||||
            return parts.length > 1 ? parts.slice(1).join(":") : id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Checks if the value returned is falsey, and if so returns a
 | 
			
		||||
         * rejected promise
 | 
			
		||||
         */
 | 
			
		||||
        function rejectIfFalsey(value, $q){
 | 
			
		||||
            if (!value){
 | 
			
		||||
                return $q.reject("Error persisting object");
 | 
			
		||||
            } else {
 | 
			
		||||
                return value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function formatError(error){
 | 
			
		||||
            if (error && error.message) {
 | 
			
		||||
                return error.message;
 | 
			
		||||
            } else if (error && typeof error === "string"){
 | 
			
		||||
                return error;
 | 
			
		||||
            } else {
 | 
			
		||||
                return "unknown error";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Display a notification message if an error has occurred during
 | 
			
		||||
         * persistence.
 | 
			
		||||
         */
 | 
			
		||||
        function notifyOnError(error, domainObject, notificationService, $q){
 | 
			
		||||
            var errorMessage = "Unable to persist " + domainObject.getModel().name;
 | 
			
		||||
            if (error) {
 | 
			
		||||
                errorMessage += ": " + formatError(error);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            notificationService.error({
 | 
			
		||||
                title: "Error persisting " + domainObject.getModel().name,
 | 
			
		||||
                hint: errorMessage || "Unknown error"
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return $q.reject(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Persist any changes which have been made to this
 | 
			
		||||
         * domain object's model.
 | 
			
		||||
@@ -71,7 +125,8 @@ define(
 | 
			
		||||
         *          if not.
 | 
			
		||||
         */
 | 
			
		||||
        PersistenceCapability.prototype.persist = function () {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
            var self = this,
 | 
			
		||||
                domainObject = this.domainObject,
 | 
			
		||||
                model = domainObject.getModel(),
 | 
			
		||||
                modified = model.modified,
 | 
			
		||||
                persistenceService = this.persistenceService,
 | 
			
		||||
@@ -87,9 +142,13 @@ define(
 | 
			
		||||
            // ...and persist
 | 
			
		||||
            return persistenceFn.apply(persistenceService, [
 | 
			
		||||
                this.getSpace(),
 | 
			
		||||
                domainObject.getId(),
 | 
			
		||||
                getKey(domainObject.getId()),
 | 
			
		||||
                domainObject.getModel()
 | 
			
		||||
            ]);
 | 
			
		||||
            ]).then(function(result){
 | 
			
		||||
                return rejectIfFalsey(result, self.$q);
 | 
			
		||||
            }).catch(function(error){
 | 
			
		||||
                return notifyOnError(error, domainObject, self.notificationService, self.$q);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -130,7 +189,8 @@ define(
 | 
			
		||||
         *          be used to persist this object
 | 
			
		||||
         */
 | 
			
		||||
        PersistenceCapability.prototype.getSpace = function () {
 | 
			
		||||
            return this.space;
 | 
			
		||||
            var id = this.domainObject.getId();
 | 
			
		||||
            return this.identifierService.parse(id).getSpace();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return PersistenceCapability;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								platform/core/src/identifiers/Identifier.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								platform/core/src/identifiers/Identifier.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var SEPARATOR = ":";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides an interface for interpreting domain object identifiers;
 | 
			
		||||
         * in particular, parses out persistence space/key pairs associated
 | 
			
		||||
         * with the domain object.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/core
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {string} id the domain object identifier
 | 
			
		||||
         * @param {string} defaultSpace the persistence space to use if
 | 
			
		||||
         *        one is not encoded in the identifier
 | 
			
		||||
         */
 | 
			
		||||
        function Identifier(id, defaultSpace) {
 | 
			
		||||
            var separatorIndex = id.indexOf(SEPARATOR);
 | 
			
		||||
 | 
			
		||||
            if (separatorIndex > -1) {
 | 
			
		||||
                this.key = id.substring(separatorIndex + 1);
 | 
			
		||||
                this.space = id.substring(0, separatorIndex);
 | 
			
		||||
                this.definedSpace = this.space;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.key = id;
 | 
			
		||||
                this.space = defaultSpace;
 | 
			
		||||
                this.definedSpace = undefined;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the key under which the identified domain object's model
 | 
			
		||||
         * should be persisted, within its persistence space.
 | 
			
		||||
         * @returns {string} the key within its persistence space
 | 
			
		||||
         */
 | 
			
		||||
        Identifier.prototype.getKey = function () {
 | 
			
		||||
            return this.key;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the space in which the identified domain object's model should
 | 
			
		||||
         * be persisted.
 | 
			
		||||
         * @returns {string} the persistence space
 | 
			
		||||
         */
 | 
			
		||||
        Identifier.prototype.getSpace = function () {
 | 
			
		||||
            return this.space;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the persistence space, if any, which has been explicitly
 | 
			
		||||
         * encoded in this domain object's identifier. Returns undefined
 | 
			
		||||
         * if no such space has been specified.
 | 
			
		||||
         * @returns {string} the persistence space, or undefined
 | 
			
		||||
         */
 | 
			
		||||
        Identifier.prototype.getDefinedSpace = function () {
 | 
			
		||||
            return this.definedSpace;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return Identifier;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										66
									
								
								platform/core/src/identifiers/IdentifierProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								platform/core/src/identifiers/IdentifierProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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(
 | 
			
		||||
    ["uuid", "./Identifier"],
 | 
			
		||||
    function (uuid, Identifier) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Parses and generates domain object identifiers.
 | 
			
		||||
         * @param {string} defaultSpace the default persistence space
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof {platform/core}
 | 
			
		||||
         */
 | 
			
		||||
        function IdentifierProvider(defaultSpace) {
 | 
			
		||||
            this.defaultSpace = defaultSpace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Generate a new domain object identifier. A persistence space
 | 
			
		||||
         * may optionally be included; if not specified, no space will
 | 
			
		||||
         * be encoded into the identifier.
 | 
			
		||||
         * @param {string} [space] the persistence space to encode
 | 
			
		||||
         *        in this identifier
 | 
			
		||||
         * @returns {string} a new domain object identifier
 | 
			
		||||
         */
 | 
			
		||||
        IdentifierProvider.prototype.generate = function (space) {
 | 
			
		||||
            var id = uuid();
 | 
			
		||||
            if (space !== undefined) {
 | 
			
		||||
                id = space + ":" + id;
 | 
			
		||||
            }
 | 
			
		||||
            return id;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Parse a domain object identifier to examine its component
 | 
			
		||||
         * parts (e.g. its persistence space.)
 | 
			
		||||
         * @returns {platform/core.Identifier} the parsed identifier
 | 
			
		||||
         */
 | 
			
		||||
        IdentifierProvider.prototype.parse = function (id) {
 | 
			
		||||
            return new Identifier(id, this.defaultSpace);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return IdentifierProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -33,6 +33,15 @@ define(
 | 
			
		||||
         * A model service which reads domain object models from an external
 | 
			
		||||
         * persistence service.
 | 
			
		||||
         *
 | 
			
		||||
         * Identifiers will be interpreted as follows:
 | 
			
		||||
         * * If no colon is present, the model will be read from the default
 | 
			
		||||
         *   persistence space.
 | 
			
		||||
         * * If a colon is present, everything before the first colon will be
 | 
			
		||||
         *   taken to refer to the persistence space, and everything after
 | 
			
		||||
         *   will be taken to be that model's key within this space. (If
 | 
			
		||||
         *   no such space exists within the `persistenceService`, that
 | 
			
		||||
         *   identifier will simply be ignored.)
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/core
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {ModelService}
 | 
			
		||||
@@ -41,39 +50,26 @@ define(
 | 
			
		||||
         * @param $q Angular's $q service, for working with promises
 | 
			
		||||
         * @param {function} now a function which provides the current time
 | 
			
		||||
         * @param {string} space the name of the persistence space(s)
 | 
			
		||||
         *        from which models should be retrieved.
 | 
			
		||||
         * @param {string} spaces additional persistence spaces to use
 | 
			
		||||
         *        from which models should be retrieved by default
 | 
			
		||||
         */
 | 
			
		||||
        function PersistedModelProvider(persistenceService, $q, now, space, spaces) {
 | 
			
		||||
        function PersistedModelProvider(persistenceService, $q, now, space) {
 | 
			
		||||
            this.persistenceService = persistenceService;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.spaces = [space].concat(spaces || []);
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Take the most recently modified model, for cases where
 | 
			
		||||
        // multiple persistence spaces return models.
 | 
			
		||||
        function takeMostRecent(modelA, modelB) {
 | 
			
		||||
            return (!modelB || modelB.modified === undefined) ? modelA :
 | 
			
		||||
                    (!modelA || modelA.modified === undefined) ? modelB :
 | 
			
		||||
                            modelB.modified > modelA.modified ? modelB :
 | 
			
		||||
                                    modelA;
 | 
			
		||||
            this.defaultSpace = space;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        PersistedModelProvider.prototype.getModels = function (ids) {
 | 
			
		||||
            var persistenceService = this.persistenceService,
 | 
			
		||||
                $q = this.$q,
 | 
			
		||||
                spaces = this.spaces,
 | 
			
		||||
                space = this.space,
 | 
			
		||||
                now = this.now;
 | 
			
		||||
                now = this.now,
 | 
			
		||||
                defaultSpace = this.defaultSpace,
 | 
			
		||||
                parsedIds;
 | 
			
		||||
 | 
			
		||||
            // Load a single object model from any persistence spaces
 | 
			
		||||
            function loadModel(id) {
 | 
			
		||||
                return $q.all(spaces.map(function (space) {
 | 
			
		||||
                    return persistenceService.readObject(space, id);
 | 
			
		||||
                })).then(function (models) {
 | 
			
		||||
                    return models.reduce(takeMostRecent);
 | 
			
		||||
                });
 | 
			
		||||
            function loadModel(parsedId) {
 | 
			
		||||
                return persistenceService
 | 
			
		||||
                        .readObject(parsedId.space, parsedId.key);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Ensure that models read from persistence have some
 | 
			
		||||
@@ -88,24 +84,43 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Package the result as id->model
 | 
			
		||||
            function packageResult(models) {
 | 
			
		||||
            function packageResult(parsedIds, models) {
 | 
			
		||||
                var result = {};
 | 
			
		||||
                ids.forEach(function (id, index) {
 | 
			
		||||
                parsedIds.forEach(function (parsedId, index) {
 | 
			
		||||
                    var id = parsedId.id;
 | 
			
		||||
                    if (models[index]) {
 | 
			
		||||
                        result[id] = addPersistedTimestamp(models[index]);
 | 
			
		||||
                        result[id] = models[index];
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Filter out "namespaced" identifiers; these are
 | 
			
		||||
            // not expected to be found in database. See WTD-659.
 | 
			
		||||
            ids = ids.filter(function (id) {
 | 
			
		||||
                return id.indexOf(":") === -1;
 | 
			
		||||
            function loadModels(parsedIds) {
 | 
			
		||||
                return $q.all(parsedIds.map(loadModel))
 | 
			
		||||
                    .then(function (models) {
 | 
			
		||||
                        return packageResult(
 | 
			
		||||
                            parsedIds,
 | 
			
		||||
                            models.map(addPersistedTimestamp)
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function restrictToSpaces(spaces) {
 | 
			
		||||
                return parsedIds.filter(function (parsedId) {
 | 
			
		||||
                    return spaces.indexOf(parsedId.space) !== -1;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            parsedIds = ids.map(function (id) {
 | 
			
		||||
                var parts = id.split(":");
 | 
			
		||||
                return (parts.length > 1) ?
 | 
			
		||||
                        { id: id, space: parts[0], key: parts.slice(1).join(":") } :
 | 
			
		||||
                        { id: id, space: defaultSpace, key: id };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Give a promise for all persistence lookups...
 | 
			
		||||
            return $q.all(ids.map(loadModel)).then(packageResult);
 | 
			
		||||
            return persistenceService.listSpaces()
 | 
			
		||||
                .then(restrictToSpaces)
 | 
			
		||||
                .then(loadModels);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return PersistedModelProvider;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@
 | 
			
		||||
/*global define,Promise*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../objects/DomainObjectImpl', 'uuid'],
 | 
			
		||||
    function (DomainObjectImpl, uuid) {
 | 
			
		||||
    ['../objects/DomainObjectImpl'],
 | 
			
		||||
    function (DomainObjectImpl) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -39,12 +39,15 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/core
 | 
			
		||||
         * @param $injector Angular's `$injector`
 | 
			
		||||
         * @param {CapabilityService} capabilityService the service which will
 | 
			
		||||
         *        provide instantiated domain objects with their capabilities
 | 
			
		||||
         * @param {IdentifierService} identifierService service to generate
 | 
			
		||||
         *        new identifiers
 | 
			
		||||
         */
 | 
			
		||||
        function Instantiate(capabilityService) {
 | 
			
		||||
        function Instantiate(capabilityService, identifierService) {
 | 
			
		||||
            return function (model, id) {
 | 
			
		||||
                var capabilities = capabilityService.getCapabilities(model);
 | 
			
		||||
                id = id || uuid();
 | 
			
		||||
                id = id || identifierService.generate();
 | 
			
		||||
                return new DomainObjectImpl(id, model, capabilities);
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,19 +28,40 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The 'instantiation' capability", function () {
 | 
			
		||||
            var mockInjector,
 | 
			
		||||
                mockIdentifierService,
 | 
			
		||||
                mockInstantiate,
 | 
			
		||||
                mockIdentifier,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                instantiation;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockInjector = jasmine.createSpyObj("$injector", ["get"]);
 | 
			
		||||
                mockInstantiate = jasmine.createSpy("instantiate");
 | 
			
		||||
                mockIdentifierService = jasmine.createSpyObj(
 | 
			
		||||
                    'identifierService',
 | 
			
		||||
                    [ 'parse', 'generate' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockIdentifier = jasmine.createSpyObj(
 | 
			
		||||
                    'identifier',
 | 
			
		||||
                    [ 'getSpace', 'getKey', 'getDefinedSpace' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    'domainObject',
 | 
			
		||||
                    [ 'getId', 'getCapability', 'getModel' ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockInjector.get.andCallFake(function (key) {
 | 
			
		||||
                    return key === 'instantiate' ?
 | 
			
		||||
                            mockInstantiate : undefined;
 | 
			
		||||
                });
 | 
			
		||||
                mockIdentifierService.parse.andReturn(mockIdentifier);
 | 
			
		||||
                mockIdentifierService.generate.andReturn("some-id");
 | 
			
		||||
 | 
			
		||||
                instantiation = new InstantiationCapability(mockInjector);
 | 
			
		||||
                instantiation = new InstantiationCapability(
 | 
			
		||||
                    mockInjector,
 | 
			
		||||
                    mockIdentifierService,
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +80,8 @@ define(
 | 
			
		||||
                mockInstantiate.andReturn(mockDomainObject);
 | 
			
		||||
                expect(instantiation.instantiate(testModel))
 | 
			
		||||
                    .toBe(mockDomainObject);
 | 
			
		||||
                expect(mockInstantiate).toHaveBeenCalledWith(testModel);
 | 
			
		||||
                expect(mockInstantiate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testModel, jasmine.any(String));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
/*jslint es5: true */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * PersistenceCapabilitySpec. Created by vwoeltje on 11/6/14.
 | 
			
		||||
@@ -31,25 +32,56 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The persistence capability", function () {
 | 
			
		||||
            var mockPersistenceService,
 | 
			
		||||
                mockIdentifierService,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockIdentifier,
 | 
			
		||||
                mockNofificationService,
 | 
			
		||||
                mockQ,
 | 
			
		||||
                id = "object id",
 | 
			
		||||
                model = { someKey: "some value"},
 | 
			
		||||
                model,
 | 
			
		||||
                SPACE = "some space",
 | 
			
		||||
                persistence;
 | 
			
		||||
                persistence,
 | 
			
		||||
                happyPromise;
 | 
			
		||||
 | 
			
		||||
            function asPromise(value) {
 | 
			
		||||
            function asPromise(value, doCatch) {
 | 
			
		||||
                return (value || {}).then ? value : {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return asPromise(callback(value));
 | 
			
		||||
                    },
 | 
			
		||||
                    catch: function(callback) {
 | 
			
		||||
                        //Define a default 'happy' catch, that skips over the
 | 
			
		||||
                        // catch callback
 | 
			
		||||
                        return doCatch ? asPromise(callback(value)): asPromise(value);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                happyPromise = asPromise(true);
 | 
			
		||||
                model = { someKey: "some value", name: "domain object"};
 | 
			
		||||
 | 
			
		||||
                mockPersistenceService = jasmine.createSpyObj(
 | 
			
		||||
                    "persistenceService",
 | 
			
		||||
                    [ "updateObject", "readObject", "createObject", "deleteObject" ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockIdentifierService = jasmine.createSpyObj(
 | 
			
		||||
                    'identifierService',
 | 
			
		||||
                    [ 'parse', 'generate' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockIdentifier = jasmine.createSpyObj(
 | 
			
		||||
                    'identifier',
 | 
			
		||||
                    [ 'getSpace', 'getKey', 'getDefinedSpace' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockQ = jasmine.createSpyObj(
 | 
			
		||||
                    "$q",
 | 
			
		||||
                    ["reject"]
 | 
			
		||||
                );
 | 
			
		||||
                mockNofificationService = jasmine.createSpyObj(
 | 
			
		||||
                    "notificationService",
 | 
			
		||||
                    ["error"]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockDomainObject = {
 | 
			
		||||
                    getId: function () { return id; },
 | 
			
		||||
                    getModel: function () { return model; },
 | 
			
		||||
@@ -61,69 +93,104 @@ define(
 | 
			
		||||
                        model = mutator(model) || model;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                mockIdentifierService.parse.andReturn(mockIdentifier);
 | 
			
		||||
                mockIdentifier.getSpace.andReturn(SPACE);
 | 
			
		||||
                persistence = new PersistenceCapability(
 | 
			
		||||
                    mockPersistenceService,
 | 
			
		||||
                    SPACE,
 | 
			
		||||
                    mockIdentifierService,
 | 
			
		||||
                    mockNofificationService,
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("creates unpersisted objects with the persistence service", function () {
 | 
			
		||||
                // Verify precondition; no call made during constructor
 | 
			
		||||
                expect(mockPersistenceService.createObject).not.toHaveBeenCalled();
 | 
			
		||||
            describe("successful persistence", function() {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockPersistenceService.updateObject.andReturn(happyPromise);
 | 
			
		||||
                    mockPersistenceService.createObject.andReturn(happyPromise);
 | 
			
		||||
                });
 | 
			
		||||
                it("creates unpersisted objects with the persistence service", function () {
 | 
			
		||||
                    // Verify precondition; no call made during constructor
 | 
			
		||||
                    expect(mockPersistenceService.createObject).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                persistence.persist();
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
 | 
			
		||||
                expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
 | 
			
		||||
                    SPACE,
 | 
			
		||||
                    id,
 | 
			
		||||
                    model
 | 
			
		||||
                );
 | 
			
		||||
                    expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
 | 
			
		||||
                        SPACE,
 | 
			
		||||
                        id,
 | 
			
		||||
                        model
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates previously persisted objects with the persistence service", function () {
 | 
			
		||||
                    // Verify precondition; no call made during constructor
 | 
			
		||||
                    expect(mockPersistenceService.updateObject).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                    model.persisted = 12321;
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
 | 
			
		||||
                    expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
 | 
			
		||||
                        SPACE,
 | 
			
		||||
                        id,
 | 
			
		||||
                        model
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("reports which persistence space an object belongs to", function () {
 | 
			
		||||
                    expect(persistence.getSpace()).toEqual(SPACE);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates persisted timestamp on persistence", function () {
 | 
			
		||||
                    model.modified = 12321;
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(model.persisted).toEqual(12321);
 | 
			
		||||
                });
 | 
			
		||||
                it("refreshes the domain object model from persistence", function () {
 | 
			
		||||
                    var refreshModel = {someOtherKey: "some other value"};
 | 
			
		||||
                    mockPersistenceService.readObject.andReturn(asPromise(refreshModel));
 | 
			
		||||
                    persistence.refresh();
 | 
			
		||||
                    expect(model).toEqual(refreshModel);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not overwrite unpersisted changes on refresh", function () {
 | 
			
		||||
                    var refreshModel = {someOtherKey: "some other value"},
 | 
			
		||||
                        mockCallback = jasmine.createSpy();
 | 
			
		||||
                    model.modified = 2;
 | 
			
		||||
                    model.persisted = 1;
 | 
			
		||||
                    mockPersistenceService.readObject.andReturn(asPromise(refreshModel));
 | 
			
		||||
                    persistence.refresh().then(mockCallback);
 | 
			
		||||
                    expect(model).not.toEqual(refreshModel);
 | 
			
		||||
                    // Should have also indicated that no changes were actually made
 | 
			
		||||
                    expect(mockCallback).toHaveBeenCalledWith(false);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not trigger error notification on successful" +
 | 
			
		||||
                    " persistence", function () {
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(mockQ.reject).not.toHaveBeenCalled();
 | 
			
		||||
                    expect(mockNofificationService.error).not.toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            describe("unsuccessful persistence", function() {
 | 
			
		||||
                var sadPromise = {
 | 
			
		||||
                        then: function(callback){
 | 
			
		||||
                            return asPromise(callback(0), true);
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockPersistenceService.createObject.andReturn(sadPromise);
 | 
			
		||||
                });
 | 
			
		||||
                it("rejects on falsey persistence result", function () {
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(mockQ.reject).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            it("updates previously persisted objects with the persistence service", function () {
 | 
			
		||||
                // Verify precondition; no call made during constructor
 | 
			
		||||
                expect(mockPersistenceService.updateObject).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                model.persisted = 12321;
 | 
			
		||||
                persistence.persist();
 | 
			
		||||
 | 
			
		||||
                expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
 | 
			
		||||
                    SPACE,
 | 
			
		||||
                    id,
 | 
			
		||||
                    model
 | 
			
		||||
                );
 | 
			
		||||
                it("notifies user on persistence failure", function () {
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(mockQ.reject).toHaveBeenCalled();
 | 
			
		||||
                    expect(mockNofificationService.error).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("reports which persistence space an object belongs to", function () {
 | 
			
		||||
                expect(persistence.getSpace()).toEqual(SPACE);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates persisted timestamp on persistence", function () {
 | 
			
		||||
                model.modified = 12321;
 | 
			
		||||
                persistence.persist();
 | 
			
		||||
                expect(model.persisted).toEqual(12321);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("refreshes the domain object model from persistence", function () {
 | 
			
		||||
                var refreshModel = { someOtherKey: "some other value" };
 | 
			
		||||
                mockPersistenceService.readObject.andReturn(asPromise(refreshModel));
 | 
			
		||||
                persistence.refresh();
 | 
			
		||||
                expect(model).toEqual(refreshModel);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not overwrite unpersisted changes on refresh", function () {
 | 
			
		||||
                var refreshModel = { someOtherKey: "some other value" },
 | 
			
		||||
                    mockCallback = jasmine.createSpy();
 | 
			
		||||
                model.modified = 2;
 | 
			
		||||
                model.persisted = 1;
 | 
			
		||||
                mockPersistenceService.readObject.andReturn(asPromise(refreshModel));
 | 
			
		||||
                persistence.refresh().then(mockCallback);
 | 
			
		||||
                expect(model).not.toEqual(refreshModel);
 | 
			
		||||
                // Should have also indicated that no changes were actually made
 | 
			
		||||
                expect(mockCallback).toHaveBeenCalledWith(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								platform/core/test/identifiers/IdentifierProviderSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								platform/core/test/identifiers/IdentifierProviderSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/identifiers/IdentifierProvider"],
 | 
			
		||||
    function (IdentifierProvider) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("IdentifierProvider", function () {
 | 
			
		||||
            var defaultSpace,
 | 
			
		||||
                provider;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                defaultSpace = "some-default-space";
 | 
			
		||||
                provider = new IdentifierProvider(defaultSpace);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("generates unique identifiers", function () {
 | 
			
		||||
                expect(provider.generate())
 | 
			
		||||
                    .not.toEqual(provider.generate());
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows spaces to be specified for generated identifiers", function () {
 | 
			
		||||
                var specificSpace = "some-specific-space",
 | 
			
		||||
                    id = provider.generate(specificSpace);
 | 
			
		||||
                expect(id).toEqual(jasmine.any(String));
 | 
			
		||||
                expect(provider.parse(id).getDefinedSpace())
 | 
			
		||||
                    .toEqual(specificSpace);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("parses identifiers using the default space", function () {
 | 
			
		||||
                expect(provider.parse("some-unprefixed-id").getSpace())
 | 
			
		||||
                    .toEqual(defaultSpace);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										82
									
								
								platform/core/test/identifiers/IdentifierSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								platform/core/test/identifiers/IdentifierSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/identifiers/Identifier"],
 | 
			
		||||
    function (Identifier) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("A parsed domain object identifier", function () {
 | 
			
		||||
            var id,
 | 
			
		||||
                defaultSpace,
 | 
			
		||||
                identifier;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                defaultSpace = "someDefaultSpace";
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when space is encoded", function () {
 | 
			
		||||
                var idSpace, idKey, spacedId;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    idSpace = "a-specific-space";
 | 
			
		||||
                    idKey = "a-specific-key";
 | 
			
		||||
                    id = idSpace + ":" + idKey;
 | 
			
		||||
                    identifier = new Identifier(id, defaultSpace);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides the encoded space", function () {
 | 
			
		||||
                    expect(identifier.getSpace()).toEqual(idSpace);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides the key within that space", function () {
 | 
			
		||||
                    expect(identifier.getKey()).toEqual(idKey);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides the defined space", function () {
 | 
			
		||||
                    expect(identifier.getDefinedSpace()).toEqual(idSpace);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when space is not encoded", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    id = "a-generic-id";
 | 
			
		||||
                    identifier = new Identifier(id, defaultSpace);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides the default space", function () {
 | 
			
		||||
                    expect(identifier.getSpace()).toEqual(defaultSpace);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides the id as the key", function () {
 | 
			
		||||
                    expect(identifier.getKey()).toEqual(id);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides no defined space", function () {
 | 
			
		||||
                    expect(identifier.getDefinedSpace()).toEqual(undefined);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -33,13 +33,12 @@ define(
 | 
			
		||||
            var mockQ,
 | 
			
		||||
                mockPersistenceService,
 | 
			
		||||
                SPACE = "space0",
 | 
			
		||||
                spaces = [ "space1" ],
 | 
			
		||||
                modTimes,
 | 
			
		||||
                mockNow,
 | 
			
		||||
                provider;
 | 
			
		||||
 | 
			
		||||
            function mockPromise(value) {
 | 
			
		||||
                return {
 | 
			
		||||
                return (value || {}).then ? value : {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return mockPromise(callback(value));
 | 
			
		||||
                    },
 | 
			
		||||
@@ -78,13 +77,14 @@ define(
 | 
			
		||||
                            persisted: 0
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                mockPersistenceService.listSpaces
 | 
			
		||||
                    .andReturn(mockPromise([SPACE]));
 | 
			
		||||
 | 
			
		||||
                provider = new PersistedModelProvider(
 | 
			
		||||
                    mockPersistenceService,
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockNow,
 | 
			
		||||
                    SPACE,
 | 
			
		||||
                    spaces
 | 
			
		||||
                    SPACE
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@@ -103,25 +103,6 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("reads object models from multiple spaces", function () {
 | 
			
		||||
                var models;
 | 
			
		||||
 | 
			
		||||
                modTimes.space1 = {
 | 
			
		||||
                    'x': 12321
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                provider.getModels(["a", "x", "zz"]).then(function (m) {
 | 
			
		||||
                    models = m;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(models).toEqual({
 | 
			
		||||
                    a: { space: SPACE, id: "a", persisted: 0 },
 | 
			
		||||
                    x: { space: 'space1', id: "x", modified: 12321, persisted: 0 },
 | 
			
		||||
                    zz: { space: SPACE, id: "zz", persisted: 0 }
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("ensures that persisted timestamps are present", function () {
 | 
			
		||||
                var mockCallback = jasmine.createSpy("callback"),
 | 
			
		||||
                    testModels = {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,18 +29,27 @@ define(
 | 
			
		||||
        describe("The 'instantiate' service", function () {
 | 
			
		||||
 | 
			
		||||
            var mockCapabilityService,
 | 
			
		||||
                mockIdentifierService,
 | 
			
		||||
                mockCapabilityConstructor,
 | 
			
		||||
                mockCapabilityInstance,
 | 
			
		||||
                mockCapabilities,
 | 
			
		||||
                mockIdentifier,
 | 
			
		||||
                idCounter,
 | 
			
		||||
                testModel,
 | 
			
		||||
                instantiate,
 | 
			
		||||
                domainObject;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                idCounter = 0;
 | 
			
		||||
 | 
			
		||||
                mockCapabilityService = jasmine.createSpyObj(
 | 
			
		||||
                    'capabilityService',
 | 
			
		||||
                    ['getCapabilities']
 | 
			
		||||
                );
 | 
			
		||||
                mockIdentifierService = jasmine.createSpyObj(
 | 
			
		||||
                    'identifierService',
 | 
			
		||||
                    [ 'parse', 'generate' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockCapabilityConstructor = jasmine.createSpy('capability');
 | 
			
		||||
                mockCapabilityInstance = {};
 | 
			
		||||
                mockCapabilityService.getCapabilities.andReturn({
 | 
			
		||||
@@ -48,9 +57,17 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
                mockCapabilityConstructor.andReturn(mockCapabilityInstance);
 | 
			
		||||
 | 
			
		||||
                mockIdentifierService.generate.andCallFake(function (space) {
 | 
			
		||||
                    return (space ? (space + ":") : "") +
 | 
			
		||||
                            "some-id-" + (idCounter += 1);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                testModel = { someKey: "some value" };
 | 
			
		||||
 | 
			
		||||
                instantiate = new Instantiate(mockCapabilityService);
 | 
			
		||||
                instantiate = new Instantiate(
 | 
			
		||||
                    mockCapabilityService,
 | 
			
		||||
                    mockIdentifierService
 | 
			
		||||
                );
 | 
			
		||||
                domainObject = instantiate(testModel);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,9 @@
 | 
			
		||||
    "capabilities/PersistenceCapability",
 | 
			
		||||
    "capabilities/RelationshipCapability",
 | 
			
		||||
 | 
			
		||||
    "identifiers/Identifier",
 | 
			
		||||
    "identifiers/IdentifierProvider",
 | 
			
		||||
 | 
			
		||||
    "models/ModelAggregator",
 | 
			
		||||
    "models/MissingModelDecorator",
 | 
			
		||||
    "models/PersistedModelProvider",
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
                "glyph": "f",
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/MoveAction.js",
 | 
			
		||||
                "depends": ["locationService", "moveService"]
 | 
			
		||||
                "depends": ["policyService", "locationService", "moveService"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "copy",
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
                "glyph": "+",
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/CopyAction.js",
 | 
			
		||||
                "depends": ["$log", "locationService", "copyService",
 | 
			
		||||
                "depends": ["$log", "policyService", "locationService", "copyService",
 | 
			
		||||
                    "dialogService", "notificationService"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
                "glyph": "\u00E8",
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/LinkAction.js",
 | 
			
		||||
                "depends": ["locationService", "linkService"]
 | 
			
		||||
                "depends": ["policyService", "locationService", "linkService"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "follow",
 | 
			
		||||
@@ -39,6 +39,15 @@
 | 
			
		||||
                "glyph": "\u00F4",
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/GoToOriginalAction.js"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "locate",
 | 
			
		||||
                "name": "Set Primary Location",
 | 
			
		||||
                "description": "Set a domain object's primary location.",
 | 
			
		||||
                "glyph": "",
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/SetPrimaryLocationAction.js"
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "components": [
 | 
			
		||||
@@ -54,7 +63,11 @@
 | 
			
		||||
                "depends": ["contextualize", "$q", "$log"]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
        "policies": [
 | 
			
		||||
            {
 | 
			
		||||
                "category": "action",
 | 
			
		||||
                "implementation": "policies/CrossSpacePolicy.js"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "capabilities": [
 | 
			
		||||
            {
 | 
			
		||||
@@ -85,8 +98,7 @@
 | 
			
		||||
                "name": "Copy Service",
 | 
			
		||||
                "description": "Provides a service for copying objects",
 | 
			
		||||
                "implementation": "services/CopyService.js",
 | 
			
		||||
                "depends": ["$q", "creationService", "policyService",
 | 
			
		||||
                    "persistenceService", "now"]
 | 
			
		||||
                "depends": ["$q", "policyService", "now"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "locationService",
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,8 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @private
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         * @param {PolicyService} policyService the policy service to use to
 | 
			
		||||
         *        verify that variants of this action are allowed
 | 
			
		||||
         * @param {platform/entanglement.LocationService} locationService a
 | 
			
		||||
         *        service to request destinations from the user
 | 
			
		||||
         * @param {platform/entanglement.AbstractComposeService} composeService
 | 
			
		||||
@@ -71,7 +73,14 @@ define(
 | 
			
		||||
         * @param {string} [suffix] a string to display in the dialog title;
 | 
			
		||||
         *        default is "to a new location"
 | 
			
		||||
         */
 | 
			
		||||
        function AbstractComposeAction(locationService, composeService, context, verb, suffix) {
 | 
			
		||||
        function AbstractComposeAction(
 | 
			
		||||
            policyService,
 | 
			
		||||
            locationService,
 | 
			
		||||
            composeService,
 | 
			
		||||
            context,
 | 
			
		||||
            verb,
 | 
			
		||||
            suffix
 | 
			
		||||
        ) {
 | 
			
		||||
            if (context.selectedObject) {
 | 
			
		||||
                this.newParent = context.domainObject;
 | 
			
		||||
                this.object = context.selectedObject;
 | 
			
		||||
@@ -83,16 +92,27 @@ define(
 | 
			
		||||
                .getCapability('context')
 | 
			
		||||
                .getParent();
 | 
			
		||||
 | 
			
		||||
            this.context = context;
 | 
			
		||||
            this.policyService = policyService;
 | 
			
		||||
            this.locationService = locationService;
 | 
			
		||||
            this.composeService = composeService;
 | 
			
		||||
            this.verb = verb || "Compose";
 | 
			
		||||
            this.suffix = suffix || "to a new location";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        AbstractComposeAction.prototype.cloneContext = function () {
 | 
			
		||||
            var clone = {}, original = this.context;
 | 
			
		||||
            Object.keys(original).forEach(function (k) {
 | 
			
		||||
                clone[k] = original[k];
 | 
			
		||||
            });
 | 
			
		||||
            return clone;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        AbstractComposeAction.prototype.perform = function () {
 | 
			
		||||
            var dialogTitle,
 | 
			
		||||
                label,
 | 
			
		||||
                validateLocation,
 | 
			
		||||
                self = this,
 | 
			
		||||
                locationService = this.locationService,
 | 
			
		||||
                composeService = this.composeService,
 | 
			
		||||
                currentParent = this.currentParent,
 | 
			
		||||
@@ -109,7 +129,11 @@ define(
 | 
			
		||||
            label = this.verb + " To";
 | 
			
		||||
 | 
			
		||||
            validateLocation = function (newParent) {
 | 
			
		||||
                return composeService.validate(object, newParent);
 | 
			
		||||
                var newContext = self.cloneContext();
 | 
			
		||||
                newContext.selectedObject =  object;
 | 
			
		||||
                newContext.domainObject = newParent;
 | 
			
		||||
                return composeService.validate(object, newParent) &&
 | 
			
		||||
                    self.policyService.allow("action", self, newContext);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return locationService.getLocationFromUser(
 | 
			
		||||
 
 | 
			
		||||
@@ -34,18 +34,34 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         */
 | 
			
		||||
        function CopyAction($log, locationService, copyService, dialogService,
 | 
			
		||||
                            notificationService, context) {
 | 
			
		||||
        function CopyAction(
 | 
			
		||||
            $log,
 | 
			
		||||
            policyService,
 | 
			
		||||
            locationService,
 | 
			
		||||
            copyService,
 | 
			
		||||
            dialogService,
 | 
			
		||||
            notificationService,
 | 
			
		||||
            context
 | 
			
		||||
        ) {
 | 
			
		||||
            this.dialog = undefined;
 | 
			
		||||
            this.notification = undefined;
 | 
			
		||||
            this.dialogService = dialogService;
 | 
			
		||||
            this.notificationService = notificationService;
 | 
			
		||||
            this.$log = $log;
 | 
			
		||||
            //Extend the behaviour of the Abstract Compose Action
 | 
			
		||||
            AbstractComposeAction.call(this, locationService, copyService,
 | 
			
		||||
                context, "Duplicate", "to a location");
 | 
			
		||||
            AbstractComposeAction.call(
 | 
			
		||||
                this,
 | 
			
		||||
                policyService,
 | 
			
		||||
                locationService,
 | 
			
		||||
                copyService,
 | 
			
		||||
                context,
 | 
			
		||||
                "Duplicate",
 | 
			
		||||
                "to a location"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CopyAction.prototype = Object.create(AbstractComposeAction.prototype);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Updates user about progress of copy. Should not be invoked by
 | 
			
		||||
         * client code under any circumstances.
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,10 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         */
 | 
			
		||||
        function LinkAction(locationService, linkService, context) {
 | 
			
		||||
        function LinkAction(policyService, locationService, linkService, context) {
 | 
			
		||||
            AbstractComposeAction.apply(
 | 
			
		||||
                this,
 | 
			
		||||
                [locationService, linkService, context, "Link"]
 | 
			
		||||
                [policyService, locationService, linkService, context, "Link"]
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,12 +34,11 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         */
 | 
			
		||||
        function MoveAction(locationService, moveService, context) {
 | 
			
		||||
        function MoveAction(policyService, locationService, moveService, context) {
 | 
			
		||||
            AbstractComposeAction.apply(
 | 
			
		||||
                this,
 | 
			
		||||
                [locationService, moveService, context, "Move"]
 | 
			
		||||
                [policyService, locationService, moveService, context, "Move"]
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MoveAction.prototype = Object.create(AbstractComposeAction.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define */
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements the "Set Primary Location" action, which sets a
 | 
			
		||||
         * location property for objects to match their contextual
 | 
			
		||||
         * location.
 | 
			
		||||
         *
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @private
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         * @param {ActionContext} context the context in which the action
 | 
			
		||||
         *        will be performed
 | 
			
		||||
         */
 | 
			
		||||
        function SetPrimaryLocationAction(context) {
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SetPrimaryLocationAction.prototype.perform = function () {
 | 
			
		||||
            var location = this.domainObject.getCapability('location');
 | 
			
		||||
            return location.setPrimaryLocation(
 | 
			
		||||
                location.getContextualLocation()
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        SetPrimaryLocationAction.appliesTo = function (context) {
 | 
			
		||||
            var domainObject = context.domainObject;
 | 
			
		||||
            return domainObject && domainObject.hasCapability("location")
 | 
			
		||||
                && (domainObject.getModel().location === undefined);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return SetPrimaryLocationAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										73
									
								
								platform/entanglement/src/policies/CrossSpacePolicy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								platform/entanglement/src/policies/CrossSpacePolicy.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var DISALLOWED_ACTIONS = [
 | 
			
		||||
            "move",
 | 
			
		||||
            "copy"
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * This policy prevents performing move/copy/link actions across
 | 
			
		||||
         * different persistence spaces (e.g. linking to an object in
 | 
			
		||||
         * a private space from an object in a public space.)
 | 
			
		||||
         * @memberof {platform/entanglement}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Policy}
 | 
			
		||||
         */
 | 
			
		||||
        function CrossSpacePolicy() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function lookupSpace(domainObject) {
 | 
			
		||||
            var persistence = domainObject &&
 | 
			
		||||
                domainObject.getCapability("persistence");
 | 
			
		||||
            return persistence && persistence.getSpace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function isCrossSpace(context) {
 | 
			
		||||
            var domainObject = context.domainObject,
 | 
			
		||||
                selectedObject = context.selectedObject,
 | 
			
		||||
                spaces = [ domainObject, selectedObject ].map(lookupSpace);
 | 
			
		||||
            return selectedObject !== undefined &&
 | 
			
		||||
                domainObject !== undefined &&
 | 
			
		||||
                lookupSpace(domainObject) !== lookupSpace(selectedObject);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CrossSpacePolicy.prototype.allow = function (action, context) {
 | 
			
		||||
            var key = action.getMetadata().key;
 | 
			
		||||
 | 
			
		||||
            if (DISALLOWED_ACTIONS.indexOf(key) !== -1) {
 | 
			
		||||
                return !isCrossSpace(context);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CrossSpacePolicy;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -38,12 +38,9 @@ define(
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         * @implements {platform/entanglement.AbstractComposeService}
 | 
			
		||||
         */
 | 
			
		||||
        function CopyService($q, creationService, policyService, persistenceService, now) {
 | 
			
		||||
        function CopyService($q, policyService) {
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.creationService = creationService;
 | 
			
		||||
            this.policyService = policyService;
 | 
			
		||||
            this.persistenceService = persistenceService;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CopyService.prototype.validate = function (object, parentCandidate) {
 | 
			
		||||
@@ -71,7 +68,7 @@ define(
 | 
			
		||||
         */
 | 
			
		||||
        CopyService.prototype.perform = function (domainObject, parent) {
 | 
			
		||||
            var $q = this.$q,
 | 
			
		||||
                copyTask = new CopyTask(domainObject, parent, this.persistenceService, this.$q, this.now);
 | 
			
		||||
                copyTask = new CopyTask(domainObject, parent, this.policyService, this.$q);
 | 
			
		||||
            if (this.validate(domainObject, parent)) {
 | 
			
		||||
                return copyTask.perform();
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,8 @@
 | 
			
		||||
/*global define */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["uuid"],
 | 
			
		||||
    function (uuid) {
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -33,36 +33,48 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @param domainObject The object to copy
 | 
			
		||||
         * @param parent The new location of the cloned object tree
 | 
			
		||||
         * @param persistenceService
 | 
			
		||||
         * @param $q
 | 
			
		||||
         * @param now
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function CopyTask (domainObject, parent, persistenceService, $q, now){
 | 
			
		||||
        function CopyTask (domainObject, parent, policyService, $q){
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.parent = parent;
 | 
			
		||||
            this.firstClone = undefined;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.deferred = undefined;
 | 
			
		||||
            this.persistenceService = persistenceService;
 | 
			
		||||
            this.policyService = policyService;
 | 
			
		||||
            this.persisted = 0;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
            this.clones = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function composeChild(child, parent) {
 | 
			
		||||
        function composeChild(child, parent, setLocation) {
 | 
			
		||||
            //Once copied, associate each cloned
 | 
			
		||||
            // composee with its parent clone
 | 
			
		||||
            child.model.location = parent.id;
 | 
			
		||||
            parent.model.composition = parent.model.composition || [];
 | 
			
		||||
            return parent.model.composition.push(child.id);
 | 
			
		||||
 | 
			
		||||
            parent.getModel().composition.push(child.getId());
 | 
			
		||||
 | 
			
		||||
            //If a location is not specified, set it.
 | 
			
		||||
            if (setLocation && child.getModel().location === undefined) {
 | 
			
		||||
                child.getModel().location = parent.getId();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function cloneObjectModel(objectModel) {
 | 
			
		||||
            var clone = JSON.parse(JSON.stringify(objectModel));
 | 
			
		||||
 | 
			
		||||
            delete clone.composition;
 | 
			
		||||
            /**
 | 
			
		||||
             * Reset certain fields.
 | 
			
		||||
             */
 | 
			
		||||
            //If has a composition, set it to an empty array. Will be
 | 
			
		||||
            // recomposed later with the ids of its cloned children.
 | 
			
		||||
            if (clone.composition) {
 | 
			
		||||
                //Important to set it to an empty array here, otherwise
 | 
			
		||||
                // hasCapability("composition") returns false;
 | 
			
		||||
                clone.composition = [];
 | 
			
		||||
            }
 | 
			
		||||
            delete clone.persisted;
 | 
			
		||||
            delete clone.modified;
 | 
			
		||||
            delete clone.location;
 | 
			
		||||
 | 
			
		||||
            return clone;
 | 
			
		||||
        }
 | 
			
		||||
@@ -73,13 +85,10 @@ define(
 | 
			
		||||
         * result in automatic request batching by the browser.
 | 
			
		||||
         */
 | 
			
		||||
        function persistObjects(self) {
 | 
			
		||||
 | 
			
		||||
            return self.$q.all(self.clones.map(function(clone){
 | 
			
		||||
                clone.model.persisted = self.now();
 | 
			
		||||
                return self.persistenceService.createObject(clone.persistenceSpace, clone.id, clone.model)
 | 
			
		||||
                    .then(function(){
 | 
			
		||||
                        self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
 | 
			
		||||
                    });
 | 
			
		||||
                return clone.getCapability("persistence").persist().then(function(){
 | 
			
		||||
                    self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
 | 
			
		||||
                });
 | 
			
		||||
            })).then(function(){
 | 
			
		||||
                return self;
 | 
			
		||||
            });
 | 
			
		||||
@@ -89,18 +98,10 @@ define(
 | 
			
		||||
         * Will add a list of clones to the specified parent's composition
 | 
			
		||||
         */
 | 
			
		||||
        function addClonesToParent(self) {
 | 
			
		||||
            var parentClone = self.clones[self.clones.length-1];
 | 
			
		||||
 | 
			
		||||
            if (!self.parent.hasCapability('composition')){
 | 
			
		||||
                return self.$q.reject();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return self.persistenceService
 | 
			
		||||
                .updateObject(parentClone.persistenceSpace, parentClone.id, parentClone.model)
 | 
			
		||||
                .then(function(){return self.parent.getCapability("composition").add(parentClone.id);})
 | 
			
		||||
            return self.firstClone.getCapability("persistence").persist()
 | 
			
		||||
                .then(function(){self.parent.getCapability("composition").add(self.firstClone.getId());})
 | 
			
		||||
                .then(function(){return self.parent.getCapability("persistence").persist();})
 | 
			
		||||
                .then(function(){return parentClone;});
 | 
			
		||||
            // Ensure the clone of the original domainObject is returned
 | 
			
		||||
                .then(function(){return self.firstClone;});
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -112,13 +113,16 @@ define(
 | 
			
		||||
        CopyTask.prototype.copyComposees = function(composees, clonedParent, originalParent){
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            return (composees || []).reduce(function(promise, composee){
 | 
			
		||||
            return (composees || []).reduce(function(promise, originalComposee){
 | 
			
		||||
                //If the composee is composed of other
 | 
			
		||||
                // objects, chain a promise..
 | 
			
		||||
                return promise.then(function(){
 | 
			
		||||
                    // ...to recursively copy it (and its children)
 | 
			
		||||
                    return self.copy(composee, originalParent).then(function(composee){
 | 
			
		||||
                        composeChild(composee, clonedParent);
 | 
			
		||||
                    return self.copy(originalComposee, originalParent).then(function(clonedComposee){
 | 
			
		||||
                        //Compose the child within its parent. Cloned
 | 
			
		||||
                        // objects will need to also have their location
 | 
			
		||||
                        // set, however linked objects will not.
 | 
			
		||||
                        return composeChild(clonedComposee, clonedParent, clonedComposee !== originalComposee);
 | 
			
		||||
                    });
 | 
			
		||||
                });}, self.$q.when(undefined)
 | 
			
		||||
            );
 | 
			
		||||
@@ -131,29 +135,43 @@ define(
 | 
			
		||||
         * cloning objects, and composing them with their child clones
 | 
			
		||||
         * as it goes
 | 
			
		||||
         * @private
 | 
			
		||||
         * @param originalObject
 | 
			
		||||
         * @param originalParent
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         * @returns {DomainObject} If the type of the original object allows for
 | 
			
		||||
         * duplication, then a duplicate of the object, otherwise the object
 | 
			
		||||
         * itself (to allow linking to non duplicatable objects).
 | 
			
		||||
         */
 | 
			
		||||
        CopyTask.prototype.copy = function(originalObject, originalParent) {
 | 
			
		||||
        CopyTask.prototype.copy = function(originalObject) {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                modelClone = {
 | 
			
		||||
                id: uuid(),
 | 
			
		||||
                model: cloneObjectModel(originalObject.getModel()),
 | 
			
		||||
                persistenceSpace: originalParent.hasCapability('persistence') && originalParent.getCapability('persistence').getSpace()
 | 
			
		||||
            };
 | 
			
		||||
                clone;
 | 
			
		||||
 | 
			
		||||
            return this.$q.when(originalObject.useCapability('composition')).then(function(composees){
 | 
			
		||||
                self.deferred.notify({phase: "preparing"});
 | 
			
		||||
                //Duplicate the object's children, and their children, and
 | 
			
		||||
                // so on down to the leaf nodes of the tree.
 | 
			
		||||
                return self.copyComposees(composees, modelClone, originalObject).then(function (){
 | 
			
		||||
                    //Add the clone to the list of clones that will
 | 
			
		||||
                    //be returned by this function
 | 
			
		||||
                    self.clones.push(modelClone);
 | 
			
		||||
                    return modelClone;
 | 
			
		||||
            //Check if the type of the object being copied allows for
 | 
			
		||||
            // creation of new instances. If it does not, then a link to the
 | 
			
		||||
            // original will be created instead.
 | 
			
		||||
            if (this.policyService.allow("creation", originalObject.getCapability("type"))){
 | 
			
		||||
                //create a new clone of the original object. Use the
 | 
			
		||||
                // creation capability of the targetParent to create the
 | 
			
		||||
                // new clone. This will ensure that the correct persistence
 | 
			
		||||
                // space is used.
 | 
			
		||||
                clone = this.parent.useCapability("instantiation", cloneObjectModel(originalObject.getModel()));
 | 
			
		||||
 | 
			
		||||
                //Iterate through child tree
 | 
			
		||||
                return this.$q.when(originalObject.useCapability('composition')).then(function(composees){
 | 
			
		||||
                    self.deferred.notify({phase: "preparing"});
 | 
			
		||||
                    //Duplicate the object's children, and their children, and
 | 
			
		||||
                    // so on down to the leaf nodes of the tree.
 | 
			
		||||
                    //If it is a link, don't both with children
 | 
			
		||||
                    return self.copyComposees(composees, clone, originalObject).then(function (){
 | 
			
		||||
                        //Add the clone to the list of clones that will
 | 
			
		||||
                        //be returned by this function
 | 
			
		||||
                        self.clones.push(clone);
 | 
			
		||||
                        return clone;
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            } else {
 | 
			
		||||
                //Creating a link, no need to iterate children
 | 
			
		||||
                return self.$q.when(originalObject);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -172,7 +190,10 @@ define(
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            return this.copy(self.domainObject, self.parent).then(function(domainObjectClone){
 | 
			
		||||
                domainObjectClone.model.location = self.parent.getId();
 | 
			
		||||
                if (domainObjectClone !== self.domainObject) {
 | 
			
		||||
                    domainObjectClone.getModel().location = self.parent.getId();
 | 
			
		||||
                }
 | 
			
		||||
                self.firstClone = domainObjectClone;
 | 
			
		||||
                return self;
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ define(
 | 
			
		||||
        describe("Move/copy/link Actions", function () {
 | 
			
		||||
 | 
			
		||||
            var action,
 | 
			
		||||
                policyService,
 | 
			
		||||
                locationService,
 | 
			
		||||
                locationServicePromise,
 | 
			
		||||
                composeService,
 | 
			
		||||
@@ -44,6 +45,11 @@ define(
 | 
			
		||||
                newParent;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                policyService = jasmine.createSpyObj(
 | 
			
		||||
                    'policyService',
 | 
			
		||||
                    [ 'allow' ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                selectedObjectContextCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'selectedObjectContextCapability',
 | 
			
		||||
                    [
 | 
			
		||||
@@ -87,6 +93,8 @@ define(
 | 
			
		||||
                    ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                policyService.allow.andReturn(true);
 | 
			
		||||
 | 
			
		||||
                locationService
 | 
			
		||||
                    .getLocationFromUser
 | 
			
		||||
                    .andReturn(locationServicePromise);
 | 
			
		||||
@@ -124,6 +132,7 @@ define(
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    action = new AbstractComposeAction(
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        composeService,
 | 
			
		||||
                        context,
 | 
			
		||||
@@ -164,6 +173,30 @@ define(
 | 
			
		||||
                        expect(composeService.perform)
 | 
			
		||||
                            .toHaveBeenCalledWith(selectedObject, newParent);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    describe("provides a validator which", function () {
 | 
			
		||||
                        var validator;
 | 
			
		||||
 | 
			
		||||
                        beforeEach(function () {
 | 
			
		||||
                            validator = locationService.getLocationFromUser
 | 
			
		||||
                                .mostRecentCall.args[2];
 | 
			
		||||
                            composeService.validate.andReturn(true);
 | 
			
		||||
                            policyService.allow.andReturn(true);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("is sensitive to policy", function () {
 | 
			
		||||
                            expect(validator()).toBe(true);
 | 
			
		||||
                            policyService.allow.andReturn(false);
 | 
			
		||||
                            expect(validator()).toBe(false);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("is sensitive to service-specific validation", function () {
 | 
			
		||||
                            expect(validator()).toBe(true);
 | 
			
		||||
                            composeService.validate.andReturn(false);
 | 
			
		||||
                            expect(validator()).toBe(false);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@@ -175,6 +208,7 @@ define(
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    action = new AbstractComposeAction(
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        composeService,
 | 
			
		||||
                        context,
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ define(
 | 
			
		||||
        describe("Copy Action", function () {
 | 
			
		||||
 | 
			
		||||
            var copyAction,
 | 
			
		||||
                policyService,
 | 
			
		||||
                locationService,
 | 
			
		||||
                locationServicePromise,
 | 
			
		||||
                copyService,
 | 
			
		||||
@@ -50,6 +51,12 @@ define(
 | 
			
		||||
                progress = {phase: "copying", totalObjects: 10, processed: 1};
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                policyService = jasmine.createSpyObj(
 | 
			
		||||
                    'policyService',
 | 
			
		||||
                    [ 'allow' ]
 | 
			
		||||
                );
 | 
			
		||||
                policyService.allow.andReturn(true);
 | 
			
		||||
 | 
			
		||||
                selectedObjectContextCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'selectedObjectContextCapability',
 | 
			
		||||
                    [
 | 
			
		||||
@@ -142,6 +149,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                    copyAction = new CopyAction(
 | 
			
		||||
                        mockLog,
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        copyService,
 | 
			
		||||
                        dialogService,
 | 
			
		||||
@@ -201,6 +209,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                    copyAction = new CopyAction(
 | 
			
		||||
                        mockLog,
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        copyService,
 | 
			
		||||
                        dialogService,
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ define(
 | 
			
		||||
        describe("Link Action", function () {
 | 
			
		||||
 | 
			
		||||
            var linkAction,
 | 
			
		||||
                policyService,
 | 
			
		||||
                locationService,
 | 
			
		||||
                locationServicePromise,
 | 
			
		||||
                linkService,
 | 
			
		||||
@@ -44,6 +45,12 @@ define(
 | 
			
		||||
                newParent;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                policyService = jasmine.createSpyObj(
 | 
			
		||||
                    'policyService',
 | 
			
		||||
                    [ 'allow' ]
 | 
			
		||||
                );
 | 
			
		||||
                policyService.allow.andReturn(true);
 | 
			
		||||
 | 
			
		||||
                selectedObjectContextCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'selectedObjectContextCapability',
 | 
			
		||||
                    [
 | 
			
		||||
@@ -102,6 +109,7 @@ define(
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    linkAction = new LinkAction(
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        linkService,
 | 
			
		||||
                        context
 | 
			
		||||
@@ -152,6 +160,7 @@ define(
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    linkAction = new LinkAction(
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        linkService,
 | 
			
		||||
                        context
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ define(
 | 
			
		||||
        describe("Move Action", function () {
 | 
			
		||||
 | 
			
		||||
            var moveAction,
 | 
			
		||||
                policyService,
 | 
			
		||||
                locationService,
 | 
			
		||||
                locationServicePromise,
 | 
			
		||||
                moveService,
 | 
			
		||||
@@ -44,6 +45,12 @@ define(
 | 
			
		||||
                newParent;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                policyService = jasmine.createSpyObj(
 | 
			
		||||
                    'policyService',
 | 
			
		||||
                    [ 'allow' ]
 | 
			
		||||
                );
 | 
			
		||||
                policyService.allow.andReturn(true);
 | 
			
		||||
 | 
			
		||||
                selectedObjectContextCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'selectedObjectContextCapability',
 | 
			
		||||
                    [
 | 
			
		||||
@@ -102,6 +109,7 @@ define(
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    moveAction = new MoveAction(
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        moveService,
 | 
			
		||||
                        context
 | 
			
		||||
@@ -152,6 +160,7 @@ define(
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    moveAction = new MoveAction(
 | 
			
		||||
                        policyService,
 | 
			
		||||
                        locationService,
 | 
			
		||||
                        moveService,
 | 
			
		||||
                        context
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,80 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,beforeEach,it,jasmine,expect */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        '../../src/actions/SetPrimaryLocationAction',
 | 
			
		||||
        '../DomainObjectFactory'
 | 
			
		||||
    ],
 | 
			
		||||
    function (SetPrimaryLocation, domainObjectFactory) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("The 'set primary location' action", function () {
 | 
			
		||||
            var testContext,
 | 
			
		||||
                testModel,
 | 
			
		||||
                testId,
 | 
			
		||||
                mockLocationCapability,
 | 
			
		||||
                mockContextCapability;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testId = "some-id";
 | 
			
		||||
                testModel = { name: "some name" };
 | 
			
		||||
 | 
			
		||||
                mockLocationCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'location',
 | 
			
		||||
                    [ 'setPrimaryLocation', 'getContextualLocation' ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockLocationCapability.getContextualLocation.andReturn(testId);
 | 
			
		||||
 | 
			
		||||
                testContext = {
 | 
			
		||||
                    domainObject: domainObjectFactory({
 | 
			
		||||
                        capabilities: {
 | 
			
		||||
                            location: mockLocationCapability
 | 
			
		||||
                        },
 | 
			
		||||
                        model: testModel
 | 
			
		||||
                    })
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is applicable to objects with no location specified", function () {
 | 
			
		||||
                expect(SetPrimaryLocation.appliesTo(testContext))
 | 
			
		||||
                    .toBe(true);
 | 
			
		||||
                testContext.domainObject.getModel.andReturn({
 | 
			
		||||
                    location: "something",
 | 
			
		||||
                    name: "some name"
 | 
			
		||||
                });
 | 
			
		||||
                expect(SetPrimaryLocation.appliesTo(testContext))
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("sets the location contextually when performed", function () {
 | 
			
		||||
                new SetPrimaryLocation(testContext).perform();
 | 
			
		||||
                expect(mockLocationCapability.setPrimaryLocation)
 | 
			
		||||
                    .toHaveBeenCalledWith(testId);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										120
									
								
								platform/entanglement/test/policies/CrossSpacePolicySpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								platform/entanglement/test/policies/CrossSpacePolicySpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define,describe,beforeEach,it,jasmine,expect,spyOn */
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        '../../src/policies/CrossSpacePolicy',
 | 
			
		||||
        '../DomainObjectFactory'
 | 
			
		||||
    ],
 | 
			
		||||
    function (CrossSpacePolicy, domainObjectFactory) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("CrossSpacePolicy", function () {
 | 
			
		||||
            var mockAction,
 | 
			
		||||
                testActionMetadata,
 | 
			
		||||
                sameSpaceContext,
 | 
			
		||||
                crossSpaceContext,
 | 
			
		||||
                policy;
 | 
			
		||||
 | 
			
		||||
            function makeObject(space) {
 | 
			
		||||
                var mockPersistence = jasmine.createSpyObj(
 | 
			
		||||
                    'persistence',
 | 
			
		||||
                    ['getSpace']
 | 
			
		||||
                );
 | 
			
		||||
                mockPersistence.getSpace.andReturn(space);
 | 
			
		||||
                return domainObjectFactory({
 | 
			
		||||
                    id: space + ":foo",
 | 
			
		||||
                    model: {},
 | 
			
		||||
                    capabilities: { persistence: mockPersistence }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testActionMetadata = {};
 | 
			
		||||
 | 
			
		||||
                // Policy should only call passive methods, so
 | 
			
		||||
                // only define those in mocks.
 | 
			
		||||
                mockAction = jasmine.createSpyObj(
 | 
			
		||||
                    'action',
 | 
			
		||||
                    [ 'getMetadata' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockAction.getMetadata.andReturn(testActionMetadata);
 | 
			
		||||
 | 
			
		||||
                sameSpaceContext = {
 | 
			
		||||
                    domainObject: makeObject('a'),
 | 
			
		||||
                    selectedObject: makeObject('a')
 | 
			
		||||
                };
 | 
			
		||||
                crossSpaceContext = {
 | 
			
		||||
                    domainObject: makeObject('a'),
 | 
			
		||||
                    selectedObject: makeObject('b')
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                policy = new CrossSpacePolicy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            ['move', 'copy'].forEach(function (key) {
 | 
			
		||||
                describe("for " + key + " actions", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        testActionMetadata.key = key;
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("allows same-space changes", function () {
 | 
			
		||||
                        expect(policy.allow(mockAction, sameSpaceContext))
 | 
			
		||||
                            .toBe(true);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("disallows cross-space changes", function () {
 | 
			
		||||
                        expect(policy.allow(mockAction, crossSpaceContext))
 | 
			
		||||
                            .toBe(false);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("allows actions with no selectedObject", function () {
 | 
			
		||||
                        expect(policy.allow(mockAction, {
 | 
			
		||||
                            domainObject: makeObject('a')
 | 
			
		||||
                        })).toBe(true);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("for other actions", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    testActionMetadata.key = "some-other-action";
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("allows same-space and cross-space changes", function () {
 | 
			
		||||
                    expect(policy.allow(mockAction, crossSpaceContext))
 | 
			
		||||
                        .toBe(true);
 | 
			
		||||
                    expect(policy.allow(mockAction, sameSpaceContext))
 | 
			
		||||
                        .toBe(true);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("allows actions with no selectedObject", function () {
 | 
			
		||||
                    expect(policy.allow(mockAction, {
 | 
			
		||||
                        domainObject: makeObject('a')
 | 
			
		||||
                    })).toBe(true);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -63,7 +63,6 @@ define(
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    copyService = new CopyService(
 | 
			
		||||
                        null,
 | 
			
		||||
                        null,
 | 
			
		||||
                        policyService
 | 
			
		||||
                    );
 | 
			
		||||
@@ -130,47 +129,50 @@ define(
 | 
			
		||||
                    creationService,
 | 
			
		||||
                    createObjectPromise,
 | 
			
		||||
                    copyService,
 | 
			
		||||
                    mockPersistenceService,
 | 
			
		||||
                    mockNow,
 | 
			
		||||
                    object,
 | 
			
		||||
                    newParent,
 | 
			
		||||
                    copyResult,
 | 
			
		||||
                    copyFinished,
 | 
			
		||||
                    persistObjectPromise,
 | 
			
		||||
                    parentPersistenceCapability,
 | 
			
		||||
                    persistenceCapability,
 | 
			
		||||
                    instantiationCapability,
 | 
			
		||||
                    compositionCapability,
 | 
			
		||||
                    locationCapability,
 | 
			
		||||
                    resolvedValue;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    creationService = jasmine.createSpyObj(
 | 
			
		||||
                        'creationService',
 | 
			
		||||
                        ['createObject']
 | 
			
		||||
                    );
 | 
			
		||||
                    createObjectPromise = synchronousPromise(undefined);
 | 
			
		||||
                    creationService.createObject.andReturn(createObjectPromise);
 | 
			
		||||
                    policyService.allow.andReturn(true);
 | 
			
		||||
                    
 | 
			
		||||
                    mockPersistenceService = jasmine.createSpyObj(
 | 
			
		||||
                        'persistenceService',
 | 
			
		||||
                        ['createObject', 'updateObject']
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    persistObjectPromise = synchronousPromise(undefined);
 | 
			
		||||
                    mockPersistenceService.createObject.andReturn(persistObjectPromise);
 | 
			
		||||
                    mockPersistenceService.updateObject.andReturn(persistObjectPromise);
 | 
			
		||||
                    
 | 
			
		||||
                    parentPersistenceCapability = jasmine.createSpyObj(
 | 
			
		||||
                        "persistence",
 | 
			
		||||
 | 
			
		||||
                    instantiationCapability = jasmine.createSpyObj(
 | 
			
		||||
                        "instantiation",
 | 
			
		||||
                        [ "invoke" ]
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    persistenceCapability = jasmine.createSpyObj(
 | 
			
		||||
                        "persistenceCapability",
 | 
			
		||||
                        [ "persist", "getSpace" ]
 | 
			
		||||
                    );
 | 
			
		||||
                    persistenceCapability.persist.andReturn(persistObjectPromise);
 | 
			
		||||
 | 
			
		||||
                    parentPersistenceCapability.persist.andReturn(persistObjectPromise);
 | 
			
		||||
                    parentPersistenceCapability.getSpace.andReturn("testSpace");
 | 
			
		||||
                    compositionCapability = jasmine.createSpyObj(
 | 
			
		||||
                        'compositionCapability',
 | 
			
		||||
                        ['invoke', 'add']
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    mockNow = jasmine.createSpyObj("mockNow", ["now"]);
 | 
			
		||||
                    mockNow.now.andCallFake(function(){
 | 
			
		||||
                        return 1234;
 | 
			
		||||
                    });
 | 
			
		||||
                    locationCapability = jasmine.createSpyObj(
 | 
			
		||||
                        'locationCapability',
 | 
			
		||||
                        ['isLink']
 | 
			
		||||
                    );
 | 
			
		||||
                    locationCapability.isLink.andReturn(false);
 | 
			
		||||
 | 
			
		||||
                    mockDeferred = jasmine.createSpyObj('mockDeferred', ['notify', 'resolve']);
 | 
			
		||||
                    mockDeferred = jasmine.createSpyObj(
 | 
			
		||||
                        'mockDeferred',
 | 
			
		||||
                        ['notify', 'resolve', 'reject']
 | 
			
		||||
                    );
 | 
			
		||||
                    mockDeferred.notify.andCallFake(function(notification){});
 | 
			
		||||
                    mockDeferred.resolve.andCallFake(function(value){resolvedValue = value;});
 | 
			
		||||
                    mockDeferred.promise = {
 | 
			
		||||
@@ -179,7 +181,11 @@ define(
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    mockQ = jasmine.createSpyObj('mockQ', ['when', 'all', 'reject', 'defer']);
 | 
			
		||||
                    mockQ = jasmine.createSpyObj(
 | 
			
		||||
                        'mockQ',
 | 
			
		||||
                        ['when', 'all', 'reject', 'defer']
 | 
			
		||||
                    );
 | 
			
		||||
                    mockQ.reject.andReturn(synchronousPromise(undefined));
 | 
			
		||||
                    mockQ.when.andCallFake(synchronousPromise);
 | 
			
		||||
                    mockQ.all.andCallFake(function (promises) {
 | 
			
		||||
                        var result = {};
 | 
			
		||||
@@ -194,6 +200,8 @@ define(
 | 
			
		||||
 | 
			
		||||
                describe("on domain object without composition", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        var objectCopy;
 | 
			
		||||
 | 
			
		||||
                        newParent = domainObjectFactory({
 | 
			
		||||
                            name: 'newParent',
 | 
			
		||||
                            id: '456',
 | 
			
		||||
@@ -201,7 +209,9 @@ define(
 | 
			
		||||
                                composition: []
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                persistence: parentPersistenceCapability
 | 
			
		||||
                                instantiation: instantiationCapability,
 | 
			
		||||
                                persistence: persistenceCapability,
 | 
			
		||||
                                composition: compositionCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
@@ -210,31 +220,46 @@ define(
 | 
			
		||||
                            id: 'abc',
 | 
			
		||||
                            model: {
 | 
			
		||||
                                name: 'some object',
 | 
			
		||||
                                location: newParent.id,
 | 
			
		||||
                                persisted: mockNow.now()
 | 
			
		||||
                                location: '456',
 | 
			
		||||
                                someOtherAttribute: 'some other value',
 | 
			
		||||
                                embeddedObjectAttribute: {
 | 
			
		||||
                                    name: 'Some embedded object'
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                persistence: persistenceCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                        
 | 
			
		||||
                        copyService = new CopyService(mockQ, creationService, policyService, mockPersistenceService, mockNow.now);
 | 
			
		||||
 | 
			
		||||
                        objectCopy = domainObjectFactory({
 | 
			
		||||
                            name: 'object',
 | 
			
		||||
                            id: 'abc.copy.fdgdfgdf',
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                persistence: persistenceCapability,
 | 
			
		||||
                                location: locationCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        instantiationCapability.invoke.andCallFake(
 | 
			
		||||
                            function(model){
 | 
			
		||||
                                objectCopy.model = model;
 | 
			
		||||
                                return objectCopy;
 | 
			
		||||
                            }
 | 
			
		||||
                        );
 | 
			
		||||
 | 
			
		||||
                        copyService = new CopyService(mockQ, policyService);
 | 
			
		||||
                        copyResult = copyService.perform(object, newParent);
 | 
			
		||||
                        copyFinished = jasmine.createSpy('copyFinished');
 | 
			
		||||
                        copyResult.then(copyFinished);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("uses persistence service", function () {
 | 
			
		||||
                     expect(mockPersistenceService.createObject)
 | 
			
		||||
                     .toHaveBeenCalledWith(parentPersistenceCapability.getSpace(), jasmine.any(String), object.getModel());
 | 
			
		||||
 | 
			
		||||
                     expect(persistObjectPromise.then)
 | 
			
		||||
                     .toHaveBeenCalledWith(jasmine.any(Function));
 | 
			
		||||
                     });
 | 
			
		||||
                    it("uses persistence capability", function () {
 | 
			
		||||
                        expect(persistenceCapability.persist)
 | 
			
		||||
                            .toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                    
 | 
			
		||||
                    it("deep clones object model", function () {
 | 
			
		||||
                        //var newModel = creationService
 | 
			
		||||
                        var newModel = mockPersistenceService
 | 
			
		||||
                            .createObject
 | 
			
		||||
                            .mostRecentCall
 | 
			
		||||
                            .args[2];
 | 
			
		||||
                        var newModel = copyFinished.calls[0].args[0].getModel();
 | 
			
		||||
                        expect(newModel).toEqual(object.model);
 | 
			
		||||
                        expect(newModel).not.toBe(object.model);
 | 
			
		||||
                    });
 | 
			
		||||
@@ -249,27 +274,57 @@ define(
 | 
			
		||||
                describe("on domainObject with composition", function () {
 | 
			
		||||
                    var newObject,
 | 
			
		||||
                        childObject,
 | 
			
		||||
                        compositionCapability,
 | 
			
		||||
                        locationCapability,
 | 
			
		||||
                        objectClone,
 | 
			
		||||
                        childObjectClone,
 | 
			
		||||
                        compositionPromise;
 | 
			
		||||
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        var invocationCount = 0,
 | 
			
		||||
                            objectClones;
 | 
			
		||||
 | 
			
		||||
                        instantiationCapability.invoke.andCallFake(
 | 
			
		||||
                            function(model){
 | 
			
		||||
                                var cloneToReturn = objectClones[invocationCount++];
 | 
			
		||||
                                cloneToReturn.model = model;
 | 
			
		||||
                                return cloneToReturn;
 | 
			
		||||
                            }
 | 
			
		||||
                        );
 | 
			
		||||
 | 
			
		||||
                        locationCapability = jasmine.createSpyObj('locationCapability', ['isLink']);
 | 
			
		||||
                        locationCapability.isLink.andReturn(true);
 | 
			
		||||
                        newParent = domainObjectFactory({
 | 
			
		||||
                            name: 'newParent',
 | 
			
		||||
                            id: '456',
 | 
			
		||||
                            model: {
 | 
			
		||||
                                composition: []
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                instantiation: instantiationCapability,
 | 
			
		||||
                                persistence: persistenceCapability,
 | 
			
		||||
                                composition: compositionCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        childObject = domainObjectFactory({
 | 
			
		||||
                            name: 'childObject',
 | 
			
		||||
                            id: 'def',
 | 
			
		||||
                            model: {
 | 
			
		||||
                                name: 'a child object'
 | 
			
		||||
                                name: 'a child object',
 | 
			
		||||
                                location: 'abc'
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                persistence: persistenceCapability,
 | 
			
		||||
                                location: locationCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                        compositionCapability = jasmine.createSpyObj(
 | 
			
		||||
                            'compositionCapability',
 | 
			
		||||
                            ['invoke', 'add']
 | 
			
		||||
                        );
 | 
			
		||||
 | 
			
		||||
                        childObjectClone = domainObjectFactory({
 | 
			
		||||
                            name: 'childObject',
 | 
			
		||||
                            id: 'def.clone',
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                persistence: persistenceCapability,
 | 
			
		||||
                                location: locationCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        compositionPromise = jasmine.createSpyObj(
 | 
			
		||||
                            'compositionPromise',
 | 
			
		||||
                            ['then']
 | 
			
		||||
@@ -280,7 +335,7 @@ define(
 | 
			
		||||
                            .andReturn(synchronousPromise([childObject]));
 | 
			
		||||
 | 
			
		||||
                        object = domainObjectFactory({
 | 
			
		||||
                            name: 'object',
 | 
			
		||||
                            name: 'some object',
 | 
			
		||||
                            id: 'abc',
 | 
			
		||||
                            model: {
 | 
			
		||||
                                name: 'some object',
 | 
			
		||||
@@ -288,36 +343,27 @@ define(
 | 
			
		||||
                                location: 'testLocation'
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                instantiation: instantiationCapability,
 | 
			
		||||
                                composition: compositionCapability,
 | 
			
		||||
                                location: locationCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                        newObject = domainObjectFactory({
 | 
			
		||||
                            name: 'object',
 | 
			
		||||
                            id: 'abc2',
 | 
			
		||||
                            model: {
 | 
			
		||||
                                name: 'some object',
 | 
			
		||||
                                composition: []
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                composition: compositionCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                        newParent = domainObjectFactory({
 | 
			
		||||
                            name: 'newParent',
 | 
			
		||||
                            id: '456',
 | 
			
		||||
                            model: {
 | 
			
		||||
                                composition: []
 | 
			
		||||
                            },
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                composition: compositionCapability,
 | 
			
		||||
                                persistence: parentPersistenceCapability
 | 
			
		||||
                                location: locationCapability,
 | 
			
		||||
                                persistence: persistenceCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        createObjectPromise = synchronousPromise(newObject);
 | 
			
		||||
                        creationService.createObject.andReturn(createObjectPromise);
 | 
			
		||||
                        copyService = new CopyService(mockQ, creationService, policyService, mockPersistenceService, mockNow.now);
 | 
			
		||||
                        objectClone = domainObjectFactory({
 | 
			
		||||
                            name: 'some object',
 | 
			
		||||
                            id: 'abc.clone',
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                instantiation: instantiationCapability,
 | 
			
		||||
                                composition: compositionCapability,
 | 
			
		||||
                                location: locationCapability,
 | 
			
		||||
                                persistence: persistenceCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        objectClones = [objectClone, childObjectClone];
 | 
			
		||||
 | 
			
		||||
                        copyService = new CopyService(mockQ, policyService);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    describe("the cloning process", function(){
 | 
			
		||||
@@ -327,10 +373,9 @@ define(
 | 
			
		||||
                            copyResult.then(copyFinished);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("copies object and children in a bottom-up" +
 | 
			
		||||
                            " fashion", function () {
 | 
			
		||||
                            expect(mockPersistenceService.createObject.calls[0].args[2].name).toEqual(childObject.model.name);
 | 
			
		||||
                            expect(mockPersistenceService.createObject.calls[1].args[2].name).toEqual(object.model.name);
 | 
			
		||||
                        it("returns a promise", function () {
 | 
			
		||||
                            expect(copyResult.then).toBeDefined();
 | 
			
		||||
                            expect(copyFinished).toHaveBeenCalled();
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("returns a promise", function () {
 | 
			
		||||
@@ -338,15 +383,27 @@ define(
 | 
			
		||||
                            expect(copyFinished).toHaveBeenCalled();
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("clears modified and sets persisted", function () {
 | 
			
		||||
                            expect(copyFinished.mostRecentCall.args[0].model.modified).toBeUndefined();
 | 
			
		||||
                            expect(copyFinished.mostRecentCall.args[0].model.persisted).toBe(mockNow.now());
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it ("correctly locates cloned objects", function() {
 | 
			
		||||
                            expect(mockPersistenceService.createObject.calls[0].args[2].location).toEqual(mockPersistenceService.createObject.calls[1].args[1]);
 | 
			
		||||
                            expect(childObjectClone.getModel().location).toEqual(objectClone.getId());
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                    describe("when cloning non-creatable objects", function() {
 | 
			
		||||
                        beforeEach(function () {
 | 
			
		||||
                            policyService.allow.andCallFake(function(category){
 | 
			
		||||
                                //Return false for 'creation' policy
 | 
			
		||||
                               return category !== 'creation';
 | 
			
		||||
                            });
 | 
			
		||||
 | 
			
		||||
                            copyResult = copyService.perform(object, newParent);
 | 
			
		||||
                            copyFinished = jasmine.createSpy('copyFinished');
 | 
			
		||||
                            copyResult.then(copyFinished);
 | 
			
		||||
                        });
 | 
			
		||||
                        it ("creates link instead of clone", function() {
 | 
			
		||||
                            var copiedObject = copyFinished.calls[0].args[0];
 | 
			
		||||
                            expect(copiedObject).toBe(object);
 | 
			
		||||
                            expect(compositionCapability.add).toHaveBeenCalledWith(copiedObject.getId());
 | 
			
		||||
                            //expect(newParent.getModel().composition).toContain(copiedObject.getId());
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
@@ -355,20 +412,28 @@ define(
 | 
			
		||||
                        object = domainObjectFactory({
 | 
			
		||||
                            name: 'object',
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                type: { type: 'object' }
 | 
			
		||||
                                type: { type: 'object' },
 | 
			
		||||
                                location: locationCapability,
 | 
			
		||||
                                persistence: persistenceCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        newParent = domainObjectFactory({
 | 
			
		||||
                            name: 'parentCandidate',
 | 
			
		||||
                            capabilities: {
 | 
			
		||||
                                type: { type: 'parentCandidate' }
 | 
			
		||||
                                type: { type: 'parentCandidate' },
 | 
			
		||||
                                instantiation: instantiationCapability,
 | 
			
		||||
                                composition: compositionCapability,
 | 
			
		||||
                                persistence: persistenceCapability
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        instantiationCapability.invoke.andReturn(object);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("throws an error", function () {
 | 
			
		||||
                        var copyService =
 | 
			
		||||
                            new CopyService(mockQ, creationService, policyService, mockPersistenceService, mockNow.now);
 | 
			
		||||
                            new CopyService(mockQ, policyService);
 | 
			
		||||
 | 
			
		||||
                        function perform() {
 | 
			
		||||
                            copyService.perform(object, newParent);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@
 | 
			
		||||
    "actions/GoToOriginalAction",
 | 
			
		||||
    "actions/LinkAction",
 | 
			
		||||
    "actions/MoveAction",
 | 
			
		||||
    "actions/SetPrimaryLocationAction",
 | 
			
		||||
    "policies/CrossSpacePolicy",
 | 
			
		||||
    "services/CopyService",
 | 
			
		||||
    "services/LinkService",
 | 
			
		||||
    "services/MoveService",
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,8 @@ define(
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function WorkerService($window, workers) {
 | 
			
		||||
            var workerUrls = {};
 | 
			
		||||
            var workerUrls = {},
 | 
			
		||||
                sharedWorkers = {};
 | 
			
		||||
 | 
			
		||||
            function addWorker(worker) {
 | 
			
		||||
                var key = worker.key;
 | 
			
		||||
@@ -48,12 +49,15 @@ define(
 | 
			
		||||
                        worker.bundle.sources,
 | 
			
		||||
                        worker.scriptUrl
 | 
			
		||||
                    ].join("/");
 | 
			
		||||
                    sharedWorkers[key] = worker.shared;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            (workers || []).forEach(addWorker);
 | 
			
		||||
            this.workerUrls = workerUrls;
 | 
			
		||||
            this.sharedWorkers = sharedWorkers;
 | 
			
		||||
            this.Worker = $window.Worker;
 | 
			
		||||
            this.SharedWorker = $window.SharedWorker;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -61,12 +65,17 @@ define(
 | 
			
		||||
         * that has been registered under the `workers` category
 | 
			
		||||
         * of extension.
 | 
			
		||||
         *
 | 
			
		||||
         * This will return either a Worker or a SharedWorker,
 | 
			
		||||
         * depending on whether a `shared` flag has been specified
 | 
			
		||||
         * on the the extension definition for the referenced worker.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {string} key symbolic identifier for the worker
 | 
			
		||||
         * @returns {Worker} the running Worker
 | 
			
		||||
         * @returns {Worker | SharedWorker} the running Worker
 | 
			
		||||
         */
 | 
			
		||||
        WorkerService.prototype.run = function (key) {
 | 
			
		||||
            var scriptUrl = this.workerUrls[key],
 | 
			
		||||
                Worker = this.Worker;
 | 
			
		||||
                Worker = this.sharedWorkers[key] ?
 | 
			
		||||
                        this.SharedWorker : this.Worker;
 | 
			
		||||
            return scriptUrl && Worker && new Worker(scriptUrl);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,10 +30,14 @@ define(
 | 
			
		||||
            var mockWindow,
 | 
			
		||||
                testWorkers,
 | 
			
		||||
                mockWorker,
 | 
			
		||||
                mockSharedWorker,
 | 
			
		||||
                service;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockWindow = jasmine.createSpyObj('$window', ['Worker']);
 | 
			
		||||
                mockWindow = jasmine.createSpyObj(
 | 
			
		||||
                    '$window',
 | 
			
		||||
                    ['Worker', 'SharedWorker']
 | 
			
		||||
                );
 | 
			
		||||
                testWorkers = [
 | 
			
		||||
                    {
 | 
			
		||||
                        key: 'abc',
 | 
			
		||||
@@ -49,11 +53,19 @@ define(
 | 
			
		||||
                        key: 'xyz',
 | 
			
		||||
                        scriptUrl: 'bad.js',
 | 
			
		||||
                        bundle: { path: 'bad', sources: 'bad' }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        key: 'a-shared-worker',
 | 
			
		||||
                        shared: true,
 | 
			
		||||
                        scriptUrl: 'c.js',
 | 
			
		||||
                        bundle: { path: 'a', sources: 'b' }
 | 
			
		||||
                    }
 | 
			
		||||
                ];
 | 
			
		||||
                mockWorker = {};
 | 
			
		||||
                mockSharedWorker = {};
 | 
			
		||||
 | 
			
		||||
                mockWindow.Worker.andReturn(mockWorker);
 | 
			
		||||
                mockWindow.SharedWorker.andReturn(mockSharedWorker);
 | 
			
		||||
 | 
			
		||||
                service = new WorkerService(mockWindow, testWorkers);
 | 
			
		||||
            });
 | 
			
		||||
@@ -68,6 +80,12 @@ define(
 | 
			
		||||
                expect(mockWindow.Worker).toHaveBeenCalledWith('x/y/z.js');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows workers to be shared", function () {
 | 
			
		||||
                expect(service.run('a-shared-worker')).toBe(mockSharedWorker);
 | 
			
		||||
                expect(mockWindow.SharedWorker)
 | 
			
		||||
                    .toHaveBeenCalledWith('a/b/c.js');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("returns undefined for unknown workers", function () {
 | 
			
		||||
                expect(service.run('def')).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
                "glyph": "L",
 | 
			
		||||
                "type": "layout",
 | 
			
		||||
                "templateUrl": "templates/layout.html",
 | 
			
		||||
                "uses": [ "composition" ],
 | 
			
		||||
                "uses": [],
 | 
			
		||||
                "gestures": [ "drop" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,43 +45,8 @@ define(
 | 
			
		||||
         * @param {Scope} $scope the controller's Angular scope
 | 
			
		||||
         */
 | 
			
		||||
        function LayoutController($scope) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Utility function to copy raw positions from configuration,
 | 
			
		||||
            // without writing directly to configuration (to avoid triggering
 | 
			
		||||
            // persistence from watchers during drags).
 | 
			
		||||
            function shallowCopy(obj, keys) {
 | 
			
		||||
                var copy = {};
 | 
			
		||||
                keys.forEach(function (k) {
 | 
			
		||||
                    copy[k] = obj[k];
 | 
			
		||||
                });
 | 
			
		||||
                return copy;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Compute panel positions based on the layout's object model
 | 
			
		||||
            function lookupPanels(ids) {
 | 
			
		||||
                var configuration = $scope.configuration || {};
 | 
			
		||||
 | 
			
		||||
                // ids is read from model.composition and may be undefined;
 | 
			
		||||
                // fall back to an array if that occurs
 | 
			
		||||
                ids = ids || [];
 | 
			
		||||
 | 
			
		||||
                // Pull panel positions from configuration
 | 
			
		||||
                self.rawPositions =
 | 
			
		||||
                    shallowCopy(configuration.panels || {}, ids);
 | 
			
		||||
 | 
			
		||||
                // Clear prior computed positions
 | 
			
		||||
                self.positions = {};
 | 
			
		||||
 | 
			
		||||
                // Update width/height that we are tracking
 | 
			
		||||
                self.gridSize =
 | 
			
		||||
                    ($scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE;
 | 
			
		||||
 | 
			
		||||
                // Compute positions and add defaults where needed
 | 
			
		||||
                ids.forEach(function (id, index) {
 | 
			
		||||
                    self.populatePosition(id, index);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            var self = this,
 | 
			
		||||
                callbackCount = 0;
 | 
			
		||||
 | 
			
		||||
            // Update grid size when it changed
 | 
			
		||||
            function updateGridSize(layoutGrid) {
 | 
			
		||||
@@ -92,7 +57,7 @@ define(
 | 
			
		||||
                // Only update panel positions if this actually changed things
 | 
			
		||||
                if (self.gridSize[0] !== oldSize[0] ||
 | 
			
		||||
                        self.gridSize[1] !== oldSize[1]) {
 | 
			
		||||
                    lookupPanels(Object.keys(self.positions));
 | 
			
		||||
                    self.layoutPanels(Object.keys(self.positions));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -120,6 +85,8 @@ define(
 | 
			
		||||
                    $scope.commit("Dropped a frame.");
 | 
			
		||||
                }
 | 
			
		||||
                // Populate template-facing position for this id
 | 
			
		||||
                self.rawPositions[id] =
 | 
			
		||||
                    $scope.configuration.panels[id];
 | 
			
		||||
                self.populatePosition(id);
 | 
			
		||||
                // Layout may contain embedded views which will
 | 
			
		||||
                // listen for drops, so call preventDefault() so
 | 
			
		||||
@@ -127,6 +94,28 @@ define(
 | 
			
		||||
                e.preventDefault();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Will fetch fully contextualized composed objects, and populate
 | 
			
		||||
            // scope with them.
 | 
			
		||||
            function refreshComposition() {
 | 
			
		||||
                //Keep a track of how many composition callbacks have been made
 | 
			
		||||
                var thisCount = ++callbackCount;
 | 
			
		||||
 | 
			
		||||
                $scope.domainObject.useCapability('composition').then(function(composition){
 | 
			
		||||
                    var ids;
 | 
			
		||||
 | 
			
		||||
                    //Is this callback for the most recent composition
 | 
			
		||||
                    // request? If not, discard it. Prevents race condition
 | 
			
		||||
                    if (thisCount === callbackCount){
 | 
			
		||||
                        ids = composition.map(function (object) {
 | 
			
		||||
                                return object.getId();
 | 
			
		||||
                            }) || [];
 | 
			
		||||
 | 
			
		||||
                        $scope.composition = composition;
 | 
			
		||||
                        self.layoutPanels(ids);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // End drag; we don't want to put $scope into this
 | 
			
		||||
            // because it triggers "cpws" (copy window or scope)
 | 
			
		||||
            // errors in Angular.
 | 
			
		||||
@@ -156,8 +145,8 @@ define(
 | 
			
		||||
            // Watch for changes to the grid size in the model
 | 
			
		||||
            $scope.$watch("model.layoutGrid", updateGridSize);
 | 
			
		||||
 | 
			
		||||
            // Position panes when the model field changes
 | 
			
		||||
            $scope.$watch("model.composition", lookupPanels);
 | 
			
		||||
            // Update composed objects on screen, and position panes
 | 
			
		||||
            $scope.$watchCollection("model.composition", refreshComposition);
 | 
			
		||||
 | 
			
		||||
            // Position panes where they are dropped
 | 
			
		||||
            $scope.$on("mctDrop", handleDrop);
 | 
			
		||||
@@ -263,6 +252,43 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Utility function to copy raw positions from configuration,
 | 
			
		||||
        // without writing directly to configuration (to avoid triggering
 | 
			
		||||
        // persistence from watchers during drags).
 | 
			
		||||
        function shallowCopy(obj, keys) {
 | 
			
		||||
            var copy = {};
 | 
			
		||||
            keys.forEach(function (k) {
 | 
			
		||||
                copy[k] = obj[k];
 | 
			
		||||
            });
 | 
			
		||||
            return copy;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Compute panel positions based on the layout's object model.
 | 
			
		||||
         * Defined as member function to facilitate testing.
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        LayoutController.prototype.layoutPanels = function (ids) {
 | 
			
		||||
            var configuration = this.$scope.configuration || {},
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            // Pull panel positions from configuration
 | 
			
		||||
            this.rawPositions =
 | 
			
		||||
                shallowCopy(configuration.panels || {}, ids);
 | 
			
		||||
 | 
			
		||||
            // Clear prior computed positions
 | 
			
		||||
            this.positions = {};
 | 
			
		||||
 | 
			
		||||
            // Update width/height that we are tracking
 | 
			
		||||
            this.gridSize =
 | 
			
		||||
                (this.$scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE;
 | 
			
		||||
 | 
			
		||||
            // Compute positions and add defaults where needed
 | 
			
		||||
            ids.forEach(function (id, index) {
 | 
			
		||||
                self.populatePosition(id, index);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * End the active drag gesture. This will update the
 | 
			
		||||
         * view configuration.
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 * 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*/
 | 
			
		||||
/*global define,describe,it,expect,beforeEach,jasmine,spyOn*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/LayoutController"],
 | 
			
		||||
@@ -31,21 +31,44 @@ define(
 | 
			
		||||
                mockEvent,
 | 
			
		||||
                testModel,
 | 
			
		||||
                testConfiguration,
 | 
			
		||||
                controller;
 | 
			
		||||
                controller,
 | 
			
		||||
                mockCompositionCapability,
 | 
			
		||||
                mockComposition,
 | 
			
		||||
                mockCompositionObjects;
 | 
			
		||||
 | 
			
		||||
            function mockPromise(value){
 | 
			
		||||
                return {
 | 
			
		||||
                    then: function (thenFunc) {
 | 
			
		||||
                        return mockPromise(thenFunc(value));
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function mockDomainObject(id){
 | 
			
		||||
                return {
 | 
			
		||||
                    getId: function() {
 | 
			
		||||
                        return id;
 | 
			
		||||
                    },
 | 
			
		||||
                    useCapability: function() {
 | 
			
		||||
                        return mockCompositionCapability;
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockScope = jasmine.createSpyObj(
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    [ "$watch", "$on", "commit" ]
 | 
			
		||||
                    [ "$watch", "$watchCollection", "$on", "commit" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockEvent = jasmine.createSpyObj(
 | 
			
		||||
                    'event',
 | 
			
		||||
                    [ 'preventDefault' ]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                testModel = {
 | 
			
		||||
                    composition: [ "a", "b", "c" ]
 | 
			
		||||
                };
 | 
			
		||||
                testModel = {};
 | 
			
		||||
 | 
			
		||||
                mockComposition = ["a", "b", "c"];
 | 
			
		||||
                mockCompositionObjects = mockComposition.map(mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                testConfiguration = {
 | 
			
		||||
                    panels: {
 | 
			
		||||
@@ -56,23 +79,62 @@ define(
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mockCompositionCapability = mockPromise(mockCompositionObjects);
 | 
			
		||||
 | 
			
		||||
                mockScope.domainObject = mockDomainObject("mockDomainObject");
 | 
			
		||||
                mockScope.model = testModel;
 | 
			
		||||
                mockScope.configuration = testConfiguration;
 | 
			
		||||
                spyOn(mockScope.domainObject, "useCapability").andCallThrough();
 | 
			
		||||
 | 
			
		||||
                controller = new LayoutController(mockScope);
 | 
			
		||||
                spyOn(controller, "layoutPanels").andCallThrough();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Model changes will indicate that panel positions
 | 
			
		||||
            // may have changed, for instance.
 | 
			
		||||
            it("watches for changes to composition", function () {
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                expect(mockScope.$watchCollection).toHaveBeenCalledWith(
 | 
			
		||||
                    "model.composition",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Retrieves updated composition from composition capability", function () {
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
                expect(mockScope.domainObject.useCapability).toHaveBeenCalledWith(
 | 
			
		||||
                    "composition"
 | 
			
		||||
                );
 | 
			
		||||
                expect(controller.layoutPanels).toHaveBeenCalledWith(
 | 
			
		||||
                    mockComposition
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Is robust to concurrent changes to composition", function () {
 | 
			
		||||
                var secondMockComposition = ["a", "b", "c", "d"],
 | 
			
		||||
                    secondMockCompositionObjects = secondMockComposition.map(mockDomainObject),
 | 
			
		||||
                    firstCompositionCB,
 | 
			
		||||
                    secondCompositionCB;
 | 
			
		||||
 | 
			
		||||
                spyOn(mockCompositionCapability, "then");
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                firstCompositionCB = mockCompositionCapability.then.calls[0].args[0];
 | 
			
		||||
                secondCompositionCB = mockCompositionCapability.then.calls[1].args[0];
 | 
			
		||||
 | 
			
		||||
                //Resolve promises in reverse order
 | 
			
		||||
                secondCompositionCB(secondMockCompositionObjects);
 | 
			
		||||
                firstCompositionCB(mockCompositionObjects);
 | 
			
		||||
 | 
			
		||||
                //Expect the promise call that was initiated most recently to
 | 
			
		||||
                // be the one used to populate scope, irrespective of order that
 | 
			
		||||
                // it was eventually resolved
 | 
			
		||||
                expect(mockScope.composition).toBe(secondMockCompositionObjects);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("provides styles for frames, from configuration", function () {
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](testModel.composition);
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
                expect(controller.getFrameStyle("a")).toEqual({
 | 
			
		||||
                    top: "320px",
 | 
			
		||||
                    left: "640px",
 | 
			
		||||
@@ -85,7 +147,7 @@ define(
 | 
			
		||||
                var styleB, styleC;
 | 
			
		||||
 | 
			
		||||
                // b and c do not have configured positions
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](testModel.composition);
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                styleB = controller.getFrameStyle("b");
 | 
			
		||||
                styleC = controller.getFrameStyle("c");
 | 
			
		||||
@@ -102,7 +164,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("allows panels to be dragged", function () {
 | 
			
		||||
                // Populate scope
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](testModel.composition);
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                // Verify precondtion
 | 
			
		||||
                expect(testConfiguration.panels.b).not.toBeDefined();
 | 
			
		||||
@@ -121,7 +183,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("invokes commit after drag", function () {
 | 
			
		||||
                // Populate scope
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](testModel.composition);
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                // Do a drag
 | 
			
		||||
                controller.startDrag("b", [1, 1], [0, 0]);
 | 
			
		||||
@@ -147,7 +209,6 @@ define(
 | 
			
		||||
                expect(testConfiguration.panels.d).not.toBeDefined();
 | 
			
		||||
 | 
			
		||||
                // Notify that a drop occurred
 | 
			
		||||
                testModel.composition.push('d');
 | 
			
		||||
                mockScope.$on.mostRecentCall.args[1](
 | 
			
		||||
                    mockEvent,
 | 
			
		||||
                    'd',
 | 
			
		||||
@@ -167,7 +228,6 @@ define(
 | 
			
		||||
                mockEvent.defaultPrevented = true;
 | 
			
		||||
 | 
			
		||||
                // Notify that a drop occurred
 | 
			
		||||
                testModel.composition.push('d');
 | 
			
		||||
                mockScope.$on.mostRecentCall.args[1](
 | 
			
		||||
                    mockEvent,
 | 
			
		||||
                    'd',
 | 
			
		||||
@@ -184,7 +244,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                // White-boxy; we know which watch is which
 | 
			
		||||
                mockScope.$watch.calls[0].args[1](testModel.layoutGrid);
 | 
			
		||||
                mockScope.$watch.calls[1].args[1](testModel.composition);
 | 
			
		||||
                mockScope.$watchCollection.calls[0].args[1](testModel.composition);
 | 
			
		||||
 | 
			
		||||
                styleB = controller.getFrameStyle("b");
 | 
			
		||||
 | 
			
		||||
@@ -201,7 +261,6 @@ define(
 | 
			
		||||
                mockScope.$watch.calls[0].args[1](testModel.layoutGrid);
 | 
			
		||||
 | 
			
		||||
                // Notify that a drop occurred
 | 
			
		||||
                testModel.composition.push('d');
 | 
			
		||||
                mockScope.$on.mostRecentCall.args[1](
 | 
			
		||||
                    mockEvent,
 | 
			
		||||
                    'd',
 | 
			
		||||
@@ -215,6 +274,23 @@ define(
 | 
			
		||||
                expect(parseInt(style.width, 10)).toBeGreaterThan(63);
 | 
			
		||||
                expect(parseInt(style.height, 10)).toBeGreaterThan(31);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("updates positions of existing objects on a drop", function () {
 | 
			
		||||
                var oldStyle;
 | 
			
		||||
 | 
			
		||||
                mockScope.$watchCollection.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                oldStyle = controller.getFrameStyle("b");
 | 
			
		||||
 | 
			
		||||
                expect(oldStyle).toBeDefined();
 | 
			
		||||
 | 
			
		||||
                // ...drop event...
 | 
			
		||||
                mockScope.$on.mostRecentCall
 | 
			
		||||
                    .args[1](mockEvent, 'b', { x: 300, y: 100 });
 | 
			
		||||
 | 
			
		||||
                expect(controller.getFrameStyle("b"))
 | 
			
		||||
                    .not.toEqual(oldStyle);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@
 | 
			
		||||
                 ng-show="representation.showControls"
 | 
			
		||||
                 ng-if="axes[1].options.length > 0">
 | 
			
		||||
                <div class='form-control shell select'>
 | 
			
		||||
                    <select class="form-control input shell select"
 | 
			
		||||
                    <select class="form-control input shell"
 | 
			
		||||
                            ng-model="axes[1].active"
 | 
			
		||||
                            ng-options="option.name for option in axes[1].options">
 | 
			
		||||
                    </select>
 | 
			
		||||
@@ -160,12 +160,11 @@
 | 
			
		||||
                {{axes[0].active.name}}
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            <div class="gl-plot-x-options gl-plot-local-controls"
 | 
			
		||||
                 ng-show="representation.showControls"
 | 
			
		||||
                 ng-if="axes[0].options.length > 0">
 | 
			
		||||
                <div class='form-control shell select'>
 | 
			
		||||
                    <select class="form-control input shell select"
 | 
			
		||||
                    <select class="form-control input shell"
 | 
			
		||||
                            ng-model="axes[0].active"
 | 
			
		||||
                            ng-options="option.name for option in axes[0].options">
 | 
			
		||||
                    </select>
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,7 @@ define(
 | 
			
		||||
                    if (canvas.width !== canvas.offsetWidth ||
 | 
			
		||||
                            canvas.height !== canvas.offsetHeight) {
 | 
			
		||||
                        doDraw(scope.draw);
 | 
			
		||||
                        scope.$apply();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -181,7 +182,7 @@ define(
 | 
			
		||||
                canvas.addEventListener("webglcontextlost", fallbackFromWebGL);
 | 
			
		||||
 | 
			
		||||
                // Check for resize, on a timer
 | 
			
		||||
                activeInterval = $interval(drawIfResized, 1000);
 | 
			
		||||
                activeInterval = $interval(drawIfResized, 1000, 0, false);
 | 
			
		||||
 | 
			
		||||
                // Watch "draw" for external changes to the set of
 | 
			
		||||
                // things to be drawn.
 | 
			
		||||
 
 | 
			
		||||
@@ -45,8 +45,10 @@ define(
 | 
			
		||||
                    jasmine.createSpy("$interval");
 | 
			
		||||
                mockLog =
 | 
			
		||||
                    jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
 | 
			
		||||
                mockScope =
 | 
			
		||||
                    jasmine.createSpyObj("$scope", ["$watchCollection", "$on"]);
 | 
			
		||||
                mockScope = jasmine.createSpyObj(
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    ["$watchCollection", "$on", "$apply"]
 | 
			
		||||
                );
 | 
			
		||||
                mockElement =
 | 
			
		||||
                    jasmine.createSpyObj("element", ["find", "html"]);
 | 
			
		||||
                mockInterval.cancel = jasmine.createSpy("cancelInterval");
 | 
			
		||||
@@ -152,7 +154,9 @@ define(
 | 
			
		||||
                // Should track canvas size in an interval
 | 
			
		||||
                expect(mockInterval).toHaveBeenCalledWith(
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    jasmine.any(Number)
 | 
			
		||||
                    jasmine.any(Number),
 | 
			
		||||
                    0,
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                // Verify pre-condition
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@
 | 
			
		||||
                "properties": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "name": "Start date/time",
 | 
			
		||||
                        "control": "datetime",
 | 
			
		||||
                        "control": "timeline-datetime",
 | 
			
		||||
                        "required": true,
 | 
			
		||||
                        "property": [ "start" ],
 | 
			
		||||
                        "options": [ "SET" ]
 | 
			
		||||
@@ -83,7 +83,7 @@
 | 
			
		||||
                "properties": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "name": "Start date/time",
 | 
			
		||||
                        "control": "datetime",
 | 
			
		||||
                        "control": "timeline-datetime",
 | 
			
		||||
                        "required": true,
 | 
			
		||||
                        "property": [ "start" ],
 | 
			
		||||
                        "options": [ "SET" ]
 | 
			
		||||
@@ -271,7 +271,7 @@
 | 
			
		||||
        ],
 | 
			
		||||
        "controls": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "datetime",
 | 
			
		||||
                "key": "timeline-datetime",
 | 
			
		||||
                "templateUrl": "templates/controls/datetime.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,9 @@ define(
 | 
			
		||||
                    // Used to choose which form control to use
 | 
			
		||||
                    key: "=",
 | 
			
		||||
 | 
			
		||||
                    // Allow controls to trigger blur-like events
 | 
			
		||||
                    ngBlur: "&",
 | 
			
		||||
 | 
			
		||||
                    // The state of the form value itself
 | 
			
		||||
                    ngModel: "=",
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ define(
 | 
			
		||||
                templateUrl: templatePath,
 | 
			
		||||
 | 
			
		||||
                // Use FormController to populate/respond to changes in scope
 | 
			
		||||
                controller: FormController,
 | 
			
		||||
                controller: [ "$scope", FormController ],
 | 
			
		||||
 | 
			
		||||
                // Initial an isolate scope
 | 
			
		||||
                scope: {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ define(
 | 
			
		||||
                templateUrl: templatePath,
 | 
			
		||||
 | 
			
		||||
                // Use FormController to populate/respond to changes in scope
 | 
			
		||||
                controller: FormController,
 | 
			
		||||
                controller: [ "$scope", FormController ],
 | 
			
		||||
 | 
			
		||||
                // Initial an isolate scope
 | 
			
		||||
                scope: {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ define(
 | 
			
		||||
                // mct-form needs to watch for the form by name
 | 
			
		||||
                // in order to convey changes in $valid, $dirty, etc
 | 
			
		||||
                // up to the parent scope.
 | 
			
		||||
                mctForm.controller(mockScope);
 | 
			
		||||
                mctForm.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "mctForm",
 | 
			
		||||
@@ -56,7 +56,7 @@ define(
 | 
			
		||||
                var someState = { someKey: "some value" };
 | 
			
		||||
                mockScope.name = "someName";
 | 
			
		||||
 | 
			
		||||
                mctForm.controller(mockScope);
 | 
			
		||||
                mctForm.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](someState);
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +65,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("allows strings to be converted to RegExps", function () {
 | 
			
		||||
                // This is needed to support ng-pattern in the template
 | 
			
		||||
                mctForm.controller(mockScope);
 | 
			
		||||
                mctForm.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                // Should have added getRegExp to the scope,
 | 
			
		||||
                // to convert strings to regular expressions
 | 
			
		||||
@@ -78,7 +78,7 @@ define(
 | 
			
		||||
                    regExp;
 | 
			
		||||
 | 
			
		||||
                // Add getRegExp to scope
 | 
			
		||||
                mctForm.controller(mockScope);
 | 
			
		||||
                mctForm.controller[1](mockScope);
 | 
			
		||||
                regExp = mockScope.getRegExp(strRegExp);
 | 
			
		||||
 | 
			
		||||
                // Same object instance each time...
 | 
			
		||||
@@ -91,7 +91,7 @@ define(
 | 
			
		||||
                var regExp = /^\d+[a-d]$/;
 | 
			
		||||
 | 
			
		||||
                // Add getRegExp to scope
 | 
			
		||||
                mctForm.controller(mockScope);
 | 
			
		||||
                mctForm.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                // Should have added getRegExp to the scope,
 | 
			
		||||
                // to convert strings to regular expressions
 | 
			
		||||
@@ -100,11 +100,15 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("passes a non-whitespace regexp when no pattern is defined", function () {
 | 
			
		||||
                // If no pattern is supplied, ng-pattern should match anything
 | 
			
		||||
                mctForm.controller(mockScope);
 | 
			
		||||
                mctForm.controller[1](mockScope);
 | 
			
		||||
                expect(mockScope.getRegExp()).toEqual(/\S/);
 | 
			
		||||
                expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("explicitly declares its controller's dependency", function () {
 | 
			
		||||
                expect(mctForm.controller[0]).toEqual('$scope');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ define(
 | 
			
		||||
                // mct-form needs to watch for the form by name
 | 
			
		||||
                // in order to convey changes in $valid, $dirty, etc
 | 
			
		||||
                // up to the parent scope.
 | 
			
		||||
                mctToolbar.controller(mockScope);
 | 
			
		||||
                mctToolbar.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "mctForm",
 | 
			
		||||
@@ -56,7 +56,7 @@ define(
 | 
			
		||||
                var someState = { someKey: "some value" };
 | 
			
		||||
                mockScope.name = "someName";
 | 
			
		||||
 | 
			
		||||
                mctToolbar.controller(mockScope);
 | 
			
		||||
                mctToolbar.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](someState);
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +65,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("allows strings to be converted to RegExps", function () {
 | 
			
		||||
                // This is needed to support ng-pattern in the template
 | 
			
		||||
                mctToolbar.controller(mockScope);
 | 
			
		||||
                mctToolbar.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                // Should have added getRegExp to the scope,
 | 
			
		||||
                // to convert strings to regular expressions
 | 
			
		||||
@@ -78,7 +78,7 @@ define(
 | 
			
		||||
                    regExp;
 | 
			
		||||
 | 
			
		||||
                // Add getRegExp to scope
 | 
			
		||||
                mctToolbar.controller(mockScope);
 | 
			
		||||
                mctToolbar.controller[1](mockScope);
 | 
			
		||||
                regExp = mockScope.getRegExp(strRegExp);
 | 
			
		||||
 | 
			
		||||
                // Same object instance each time...
 | 
			
		||||
@@ -91,7 +91,7 @@ define(
 | 
			
		||||
                var regExp = /^\d+[a-d]$/;
 | 
			
		||||
 | 
			
		||||
                // Add getRegExp to scope
 | 
			
		||||
                mctToolbar.controller(mockScope);
 | 
			
		||||
                mctToolbar.controller[1](mockScope);
 | 
			
		||||
 | 
			
		||||
                // Should have added getRegExp to the scope,
 | 
			
		||||
                // to convert strings to regular expressions
 | 
			
		||||
@@ -100,11 +100,15 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("passes a non-whitespace regexp when no pattern is defined", function () {
 | 
			
		||||
                // If no pattern is supplied, ng-pattern should match anything
 | 
			
		||||
                mctToolbar.controller(mockScope);
 | 
			
		||||
                mctToolbar.controller[1](mockScope);
 | 
			
		||||
                expect(mockScope.getRegExp()).toEqual(/\S/);
 | 
			
		||||
                expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("explicitly declares its controller's dependency", function () {
 | 
			
		||||
                expect(mctToolbar.controller[0]).toEqual('$scope');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -90,12 +90,12 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            decorate($log);
 | 
			
		||||
            app.config(function ($provide) {
 | 
			
		||||
                $provide.decorator('$log', function ($delegate) {
 | 
			
		||||
            app.config(['$provide', function ($provide) {
 | 
			
		||||
                $provide.decorator('$log', ['$delegate', function ($delegate) {
 | 
			
		||||
                    decorate($delegate);
 | 
			
		||||
                    return $delegate;
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
                }]);
 | 
			
		||||
            }]);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return LogLevel;
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ define(
 | 
			
		||||
                $log = this.$log;
 | 
			
		||||
            $log.info("Bootstrapping application " + (app || {}).name);
 | 
			
		||||
            angular.element(document).ready(function () {
 | 
			
		||||
                angular.bootstrap(document, [app.name]);
 | 
			
		||||
                angular.bootstrap(document, [app.name], { strictDi: true });
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ define(
 | 
			
		||||
        ImplementationLoader.prototype.load = function loadModule(path) {
 | 
			
		||||
            var require = this.require;
 | 
			
		||||
            return new Promise(function (fulfill, reject) {
 | 
			
		||||
                require([path], fulfill, reject);
 | 
			
		||||
                require([path.replace(/\.js$/, "")], fulfill, reject);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -70,12 +70,16 @@ define(
 | 
			
		||||
                    mockDelegate[m].andCallFake(mockMethods[m]);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockApp.config.andCallFake(function (callback) {
 | 
			
		||||
                mockApp.config.andCallFake(function (arr) {
 | 
			
		||||
                    var callback = arr[1];
 | 
			
		||||
                    expect(arr[0]).toEqual('$provide');
 | 
			
		||||
                    callback(mockProvide);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockProvide.decorator.andCallFake(function (key, callback) {
 | 
			
		||||
                mockProvide.decorator.andCallFake(function (key, arr) {
 | 
			
		||||
                    // Only $log should be configured in any case
 | 
			
		||||
                    var callback = arr[1];
 | 
			
		||||
                    expect(arr[0]).toEqual('$delegate');
 | 
			
		||||
                    expect(key).toEqual('$log');
 | 
			
		||||
                    callback(mockDelegate);
 | 
			
		||||
                });
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            it("passes script names to require", function () {
 | 
			
		||||
                loader.load("xyz.js");
 | 
			
		||||
                expect(required.names).toEqual(["xyz.js"]);
 | 
			
		||||
                expect(required.names).toEqual(["xyz"]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("wraps require results in a Promise that can resolve", function () {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								platform/persistence/aggregator/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								platform/persistence/aggregator/bundle.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "components": [
 | 
			
		||||
            {
 | 
			
		||||
                "provides": "persistenceService",
 | 
			
		||||
                "type": "aggregator",
 | 
			
		||||
                "depends": [ "$q" ],
 | 
			
		||||
                "implementation": "PersistenceAggregator.js"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								platform/persistence/aggregator/src/PersistenceAggregator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								platform/persistence/aggregator/src/PersistenceAggregator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        // Return values to use when a persistence space is unknown,
 | 
			
		||||
        // and there is no appropriate provider to route to.
 | 
			
		||||
        var METHOD_DEFAULTS = {
 | 
			
		||||
            createObject: false,
 | 
			
		||||
            readObject: undefined,
 | 
			
		||||
            listObjects: [],
 | 
			
		||||
            updateObject: false,
 | 
			
		||||
            deleteObject: false
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Aggregates multiple persistence providers, such that they can be
 | 
			
		||||
         * utilized as if they were a single object. This is achieved by
 | 
			
		||||
         * routing persistence calls to an appropriate provider; the space
 | 
			
		||||
         * specified at call time is matched with the first provider (per
 | 
			
		||||
         * priority order) which reports that it provides persistence for
 | 
			
		||||
         * this space.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/persistence/aggregator
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {PersistenceService}
 | 
			
		||||
         * @param $q Angular's $q, for promises
 | 
			
		||||
         * @param {PersistenceService[]} providers the providers to aggregate
 | 
			
		||||
         */
 | 
			
		||||
        function PersistenceAggregator($q, providers) {
 | 
			
		||||
            var providerMap = {};
 | 
			
		||||
 | 
			
		||||
            function addToMap(provider) {
 | 
			
		||||
                return provider.listSpaces().then(function (spaces) {
 | 
			
		||||
                    spaces.forEach(function (space) {
 | 
			
		||||
                        providerMap[space] = providerMap[space] || provider;
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.providerMapPromise = $q.all(providers.map(addToMap))
 | 
			
		||||
                .then(function () { return providerMap; });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        PersistenceAggregator.prototype.listSpaces = function () {
 | 
			
		||||
            return this.providerMapPromise.then(function (map) {
 | 
			
		||||
                return Object.keys(map);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Object.keys(METHOD_DEFAULTS).forEach(function (method) {
 | 
			
		||||
            PersistenceAggregator.prototype[method] = function (space) {
 | 
			
		||||
                var delegateArgs = Array.prototype.slice.apply(arguments, []);
 | 
			
		||||
                return this.providerMapPromise.then(function (map) {
 | 
			
		||||
                    var provider = map[space];
 | 
			
		||||
                    return provider ?
 | 
			
		||||
                            provider[method].apply(provider, delegateArgs) :
 | 
			
		||||
                            METHOD_DEFAULTS[method];
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return PersistenceAggregator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,103 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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,beforeEach,it,jasmine,expect,spyOn */
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/PersistenceAggregator'],
 | 
			
		||||
    function (PersistenceAggregator) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var PERSISTENCE_SERVICE_METHODS = [
 | 
			
		||||
                'listSpaces',
 | 
			
		||||
                'listObjects',
 | 
			
		||||
                'createObject',
 | 
			
		||||
                'readObject',
 | 
			
		||||
                'updateObject',
 | 
			
		||||
                'deleteObject'
 | 
			
		||||
            ],
 | 
			
		||||
            WRAPPED_METHODS = PERSISTENCE_SERVICE_METHODS.filter(function (m) {
 | 
			
		||||
                return m !== 'listSpaces';
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        function fakePromise(value) {
 | 
			
		||||
            return (value || {}).then ? value : {
 | 
			
		||||
                then: function (callback) {
 | 
			
		||||
                    return fakePromise(callback(value));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        describe("PersistenceAggregator", function () {
 | 
			
		||||
            var mockQ,
 | 
			
		||||
                mockProviders,
 | 
			
		||||
                mockCallback,
 | 
			
		||||
                testSpaces,
 | 
			
		||||
                aggregator;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testSpaces = ['a', 'b', 'c'];
 | 
			
		||||
                mockQ = jasmine.createSpyObj("$q", ['all']);
 | 
			
		||||
                mockProviders = testSpaces.map(function (space) {
 | 
			
		||||
                    var mockProvider = jasmine.createSpyObj(
 | 
			
		||||
                        'provider-' + space,
 | 
			
		||||
                        PERSISTENCE_SERVICE_METHODS
 | 
			
		||||
                    );
 | 
			
		||||
                    PERSISTENCE_SERVICE_METHODS.forEach(function (m) {
 | 
			
		||||
                        mockProvider[m].andReturn(fakePromise(true));
 | 
			
		||||
                    });
 | 
			
		||||
                    mockProvider.listSpaces.andReturn(fakePromise([space]));
 | 
			
		||||
                    return mockProvider;
 | 
			
		||||
                });
 | 
			
		||||
                mockCallback = jasmine.createSpy();
 | 
			
		||||
 | 
			
		||||
                mockQ.all.andCallFake(function (fakePromises) {
 | 
			
		||||
                    var result = [];
 | 
			
		||||
                    fakePromises.forEach(function (p) {
 | 
			
		||||
                        p.then(function (v) { result.push(v); });
 | 
			
		||||
                    });
 | 
			
		||||
                    return fakePromise(result);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                aggregator = new PersistenceAggregator(mockQ, mockProviders);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes spaces for all providers", function () {
 | 
			
		||||
                aggregator.listSpaces().then(mockCallback);
 | 
			
		||||
                expect(mockCallback).toHaveBeenCalledWith(testSpaces);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            WRAPPED_METHODS.forEach(function (m) {
 | 
			
		||||
                it("redirects " + m + " calls to an appropriate provider", function () {
 | 
			
		||||
                    testSpaces.forEach(function (space, index) {
 | 
			
		||||
                        var key = 'key-' + space,
 | 
			
		||||
                            value = 'val-' + space;
 | 
			
		||||
                        expect(aggregator[m](space, key, value))
 | 
			
		||||
                            .toEqual(mockProviders[index][m]());
 | 
			
		||||
                        expect(mockProviders[index][m])
 | 
			
		||||
                            .toHaveBeenCalledWith(space, key, value);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										3
									
								
								platform/persistence/aggregator/test/suite.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								platform/persistence/aggregator/test/suite.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[
 | 
			
		||||
    "PersistenceAggregator"
 | 
			
		||||
]
 | 
			
		||||
@@ -80,7 +80,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            // Update the indicator initially, and start polling.
 | 
			
		||||
            updateIndicator();
 | 
			
		||||
            $interval(updateIndicator, interval, false);
 | 
			
		||||
            $interval(updateIndicator, interval, 0, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ElasticIndicator.prototype.getGlyph = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ define(
 | 
			
		||||
                expect(mockInterval).toHaveBeenCalledWith(
 | 
			
		||||
                    jasmine.any(Function),
 | 
			
		||||
                    testInterval,
 | 
			
		||||
                    0,
 | 
			
		||||
                    false
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user