Merge remote-tracking branch 'github/master' into open245b

...in preparation to complete merge nasa/openmctweb#257

Conflicts:
	platform/entanglement/src/actions/CopyAction.js
	platform/entanglement/src/actions/LinkAction.js
	platform/entanglement/src/actions/MoveAction.js
This commit is contained in:
Victor Woeltjen 2015-11-21 07:02:04 -08:00
commit 7974ffdda2
67 changed files with 2779 additions and 1718 deletions

View File

@ -30,6 +30,7 @@
"platform/policy",
"platform/entanglement",
"platform/search",
"platform/status",
"example/imagery",
"example/eventGenerator",

View File

@ -1288,6 +1288,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
@ -1883,6 +1899,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
@ -2090,6 +2114,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

View File

@ -32,6 +32,7 @@
<li><a href="architecture/">Architecture Overview</a></li>
<li><a href="guide/">Developer Guide</a></li>
<li><a href="tutorials/">Tutorials</a></li>
<li><a href="process/">Development Process</a></li>
</ul>
</body>
</html>

156
docs/src/process/index.md Normal file
View File

@ -0,0 +1,156 @@
# 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 |
&ast; 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 |
&ast; 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.")

View File

@ -62,7 +62,7 @@ define(
// so it's not checked for here, just formatted for display
// differently.
return (i + offset) * 1000 + firstTime * 1000 -
(domain === 'yesterday' ? ONE_DAY : 0);
(domain === 'yesterday' ? (ONE_DAY * 1000) : 0);
};
generatorData.getRangeValue = function (i, range) {

View File

@ -0,0 +1,12 @@
{
"name": "Mobile",
"description": "Allows elements with pertinence to mobile usage and development",
"extensions": {
"stylesheets": [
{
"stylesheetUrl": "css/mobile-example.css",
"priority": "mandatory"
}
]
}
}

26
example/mobile/res/config.rb Executable file
View File

@ -0,0 +1,26 @@
# Require any additional compass plugins here.
# require "compass-growl"
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "css"
sass_dir = "sass"
images_dir = "images"
javascripts_dir = "js"
# You can select your preferred output style here (can be overridden via the command line):
# :expanded, :compressed, :nested
output_style = :nested
# To enable relative paths to assets via compass helper functions. Uncomment:
relative_assets = true
# To disable debugging comments that display the original location of your selectors. Uncomment:
# line_comments = false
# If you prefer the indented syntax, you might want to regenerate this
# project again passing --syntax sass, or you can uncomment this:
# preferred_syntax = :sass
# and then run:
# sass-convert -R --from scss --to sass vfn_platform/static/sass scss && rm -rf sass && mv scss sass

View File

@ -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.
*****************************************************************************/
/*****************************************************************************
* 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.
*****************************************************************************/
/************************** FEATURES */
/************************** VERY INFLUENTIAL GLOBAL DIMENSIONS */
/************************** RATIOS */
/************************** LAYOUT */
/************************** CONTROLS */
/************************** PATHS */
/************************** TIMINGS */
/************************** LIMITS */
/*****************************************************************************
* 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.
*****************************************************************************/
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
/************************** MOBILE TREE MENU DIMENSIONS */
/************************** WINDOW DIMENSIONS FOR RWD */
/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
/************************** MEDIA QUERIES: WINDOWS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */
/*****************************************************************************
* 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.
*****************************************************************************/
/* REQUIRES mobile/_constants */
@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) {
/* line 28, ../sass/mobile-example.scss */
.create-btn-holder {
display: block !important; } }

View File

@ -0,0 +1,31 @@
/*****************************************************************************
* 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.
*****************************************************************************/
@import "../../../../platform/commonUI/general/res/sass/constants";
@import "../../../../platform/commonUI/general/res/sass/mobile/constants";
@import "../../../../platform/commonUI/general/res/sass/mobile/mixins";
@include phoneandtablet {
// Show the Create button
.create-btn-holder {
display: block !important;
}
}

View File

@ -20,15 +20,17 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="BrowseObjectController">
<div class="object-browse-bar bar l-flex">
<div class="items-select left">
<div class="object-browse-bar l-flex-row">
<div class="items-select left flex-elem l-flex-row grows">
<mct-representation key="'back-arrow'"
mct-object="domainObject"
class="l-back"></mct-representation>
<mct-representation key="'object-header'" mct-object="domainObject">
class="flex-elem l-back"></mct-representation>
<mct-representation key="'object-header'"
mct-object="domainObject"
class="l-flex-row flex-elem grows object-header">
</mct-representation>
</div>
<div class="btn-bar right">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation key="'switcher'"
mct-object="domainObject"
ng-model="representation">

View File

@ -28,30 +28,23 @@
<mct-split-pane class='abs contents'
anchor='left'>
<div class='split-pane-component treeview pane left'>
<!--<a class="mini-tab-icon anchor-left toggle-pane toggle-search mobile-hide"
title="Enable search"
ng-click="modelPaneTree.toggle()"
ng-class="{ collapsed : !modelPaneTree.visible() }"
ng-style="{ opacity: modelPaneTree.visible()? 0:1 }"></a>-->
<div class="holder holder-create-and-search abs l-mobile">
<div class="abs holder l-flex-col holder-treeview-elements">
<mct-representation key="'create-button'"
mct-object="navigatedObject"
mct-device="desktop">
class="holder flex-elem create-btn-holder">
</mct-representation>
<mct-include key="'search'"
ng-model="treeModel"
class="holder l-flex-col flex-elem search-holder"
ng-class="{ active: treeModel.search, grows: treeModel.search }">
</mct-include>
<mct-representation key="'tree'"
mct-object="domainObject"
parameters="tree"
ng-model="treeModel"
class="holder flex-elem grows vscroll tree-holder"
ng-hide="treeModel.search">
</mct-representation>
<div class='holder search-holder abs'
ng-class="{active: treeModel.search}">
<mct-include key="'search'"
ng-model="treeModel">
</mct-include>
</div>
<div class='tree-holder abs mobile-tree-holder'
ng-hide="treeModel.search">
<mct-representation key="'tree'"
mct-object="domainObject"
parameters="tree"
ng-model="treeModel">
</mct-representation>
</div>
</div>
</div>
@ -63,7 +56,7 @@
ng-click="modelPaneTree.toggle()"
ng-class="{ collapsed : !modelPaneTree.visible() }"></a>
<div class='holder holder-object-and-inspector abs l-mobile' id='content-area'
<div class='holder holder-object-and-inspector abs' id='content-area'
ng-controller="PaneController as modelPaneInspect"
ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'">

View File

@ -19,12 +19,12 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class='object-header'>
<span class='type-icon ui-symbol'>{{type.getGlyph()}}</span>
<!--span class='type-name mobile-important-hide'>{{type.getName()}}</span-->
<span class="l-elem-wrapper l-flex">
<span ng-if="parameters.mode" class='action'>{{parameters.mode}}</span>
<span class='title-label'>{{model.name}}</span>
<mct-representation key="'menu-arrow'" mct-object='domainObject'></mct-representation>
</span>
</div>
<span class='type-icon ui-symbol flex-elem'>{{type.getGlyph()}}</span>
<span class="l-elem-wrapper l-flex-row flex-elem grows">
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
<span class='title-label flex-elem flex-can-shrink'>{{model.name}}</span>
<mct-representation
key="'menu-arrow'"
mct-object='domainObject'
class="flex-elem"></mct-representation>
</span>

View File

@ -22,7 +22,8 @@
<div class='top-bar edit abs'>
<mct-representation key="'object-header'"
mct-object="domainObject"
parameters="{ mode: 'Edit' }">
parameters="{ mode: 'Edit' }"
class="l-flex-row flex-elem grows object-header">
</mct-representation>
<div class='buttons-main btn-bar buttons abs'>
<mct-representation key="'switcher'"

View File

@ -36,7 +36,6 @@
.col {
@include box-sizing(border-box);
@include clearfix;
// background: rgba(#ffcc00, 0.2);
float: left;
margin-left: $ueColMargin;
padding-left: $interiorMargin;
@ -98,22 +97,51 @@
@include display-flex;
@include flex-wrap(nowrap);
.flex-elem {
min-height: 0; // Needed to allow element to shrink within parent
position: relative;
&:not(.grows) {
@include flex(0 1 auto);
@include flex(0 0 auto);
&.flex-can-shrink {
@include flex(0 1 auto);
}
}
&.grows {
@include flex(1 1 auto);
}
}
.flex-container {
// Apply to wrapping elements, mct-includes, etc.
@include display-flex;
@include flex-wrap(nowrap);
@include flex(1 1 auto);
min-height:0;
}
}
.l-flex-row { @include flex-direction(row); }
.l-flex-col { @include flex-direction(column); }
.l-flex {
@extend .l-flex-row;
.left {
@include flex(1 1 0);
padding-right: $interiorMarginLg;
.l-flex-row {
@include flex-direction(row);
&.flex-elem { @include flex(1 1 auto); }
.flex-elem {
height: inherit;
line-height: inherit;
min-width: 0;
}
.flex-container { @include flex-direction(row); }
}
.l-flex-col {
@include flex-direction(column);
.flex-elem {
min-height: 0;
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
}
.flex-container { @include flex-direction(column); }
}
.flex-fixed {
@include flex(0 0 auto);
}
.flex-justify-end {
@include justify-content(flex-end);
}

View File

@ -35,15 +35,13 @@ $ltGamma: 20%;
$btnFontSizeToH: 0.45;
/************************** LAYOUT */
$ueTopBarH: 24px; // Change when breadcrumb is enabled
$ueTopBarH: 24px;
$ueTopBarEditH: 30px;
$ueTopBarBtnH: 35px;
$ueFooterH: 25px;
$ueColMargin: 1.5%;
$ueAppLogoW: 105px;
$ueEditToolBarH: 25px;
$ueBrowseLeftPaneTreeW: 25%;
$ueBrowseRightPaneInspectW: 20%;
$ueCollapsedPaneEdgeM: 22px;
$uePaneMiniTabH: $ueTopBarH;
$uePaneMiniTabW: 9px;
@ -51,6 +49,15 @@ $uePaneMiniTabCollapsedW: 11px;
$ueEditLeftPaneW: 75%;
$treeSearchInputBarH: 25px;
$ueTimeControlH: (33px, 20px, 20px);
// Panes
$ueBrowseLeftPaneTreeMinW: 150px;
$ueBrowseLeftPaneTreeMaxW: 35%;
$ueBrowseLeftPaneTreeW: 25%;
$ueBrowseRightPaneInspectMinW: 200px;
$ueBrowseRightPaneInspectMaxW: 35%;
$ueBrowseRightPaneInspectW: 20%;
$ueDesktopMinW: 600px;
// Overlay
$ovrTopBarH: 45px;
$ovrFooterH: 24px;

View File

@ -142,6 +142,16 @@ mct-container {
display: none !important;
}
.off {
visibility: hidden;
opacity: 0;
height: 0;
margin: 0;
padding: 0;
border: 0;
margin: 0 !important;
}
.sep {
color: rgba(#fff, 0.2);
}

View File

@ -127,7 +127,6 @@ label.checkbox.custom {
&:before {
@include border-radius($basicCr * .75);
background: $bg;
//border-bottom: 1px solid lighten($bg, 10%);
@include box-shadow(inset rgba(black, 0.4) 0 1px 2px);
box-sizing: border-box;
content: " ";
@ -202,7 +201,6 @@ label.checkbox.custom {
/******************************************************** OBJECT-HEADER */
.object-header {
//@include test();
font-size: 1em;
> .type-icon {
@ -213,8 +211,6 @@ label.checkbox.custom {
}
.l-elem-wrapper {
//@include test(#66f, 0.2);
@include justify-content(flex-start);
mct-representation {
// Holds the context-available item
// Must have min-width to make flex work properly
@ -228,19 +224,15 @@ label.checkbox.custom {
}
.title-label {
//@include test(green, 0.9);
color: $colorObjHdrTxt;
@include ellipsize();
//color: pushBack($colorBodyFg, 40%);
@include webkitProp(flex, '0 1 auto');
padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text.
//position: relative;
}
.context-available {
font-size: 0.7em;
@include webkitProp(flex, '0 0 1');
//margin-right: $interiorMargin;
}
@include desktop {
@ -303,7 +295,6 @@ label.checkbox.custom {
@include border-radius($basicCr);
@include boxIncised(0.3, 4px);
background: $colorProgressBarOuter;
//border:1px solid $colorProgressBarOuter;
.progress-amt {
@include border-radius($basicCr);
@include boxShdw();
@ -343,8 +334,6 @@ label.checkbox.custom {
.slider {
$knobH: 100%; //14px;
.slot {
// @include border-radius($basicCr * .75);
//@include sliderTrack();
width: auto;
position: absolute;
top: 0;
@ -400,14 +389,11 @@ label.checkbox.custom {
width: 230px;
.l-month-year-pager {
$pagerW: 20px;
//@include test();
//font-size: 0.8rem;
height: $r1H;
margin-bottom: $interiorMargin;
position: relative;
.pager,
.val {
//@include test(red);
@extend .abs;
}
.pager {
@ -454,7 +440,6 @@ label.checkbox.custom {
}
li {
@include flex(1 0);
//@include test();
margin-left: 1px;
padding: $interiorMargin;
text-align: center;
@ -512,8 +497,6 @@ label.checkbox.custom {
@include background-image(linear-gradient(lighten($bg, $gr), $bg 20px));
@include border-radius(2px);
@include box-sizing(border-box);
//@include boxShdwSubtle();
//border-top: 1px solid lighten($bg, 20%);
&:hover {
@include background-image(linear-gradient(lighten($hc, $gr), $hc 20px));
}

View File

@ -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 {

View File

@ -29,7 +29,7 @@
//@include test(red);
display: block;
position: absolute;
z-index: 1;
z-index: 3;
&:after {
// The handle
content:"";
@ -43,7 +43,6 @@
}
}
&:active {
//@include test();
&:after {
background-color: $colorSplitterActive !important;
}
@ -122,11 +121,3 @@
}
}
}
/*.browse-area .splitter {
top: 0; //$ueTopBarH + $interiorMarginLg;
}
.edit-area .splitter {
top: 0;
}*/

View File

@ -20,6 +20,8 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* REQUIRES /platform/commonUI/general/res/sass/_constants.scss */
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
$mobileListIconSize: 30px;
$mobileTitleDescH: 35px;
@ -32,51 +34,31 @@ $mobileTreeItemH: 35px;
$mobileTreeItemIndent: 20px;
$mobileTreeRightArrowW: 30px;
/************************** WINDOW DIMENSIONS FOR RWD */
/************************** DEVICE WIDTHS */
// IMPORTANT! Usage assumes that ranges are mutually exclusive and have no gaps
$phoMaxW: 514px;
$phoMaxH: 740px;
$tabMinW: 515px;
$tabMaxW: 799px;
$tabMinH: 741px;
$tabMaxH: 1024px;
$compMinW: 800px;
$compMinH: 1025px;
$tabMaxW: 1280px;
$desktopMinW: 1281px;
/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
$screenPortrait: "screen and (orientation: portrait)";
$screenLandscape: "screen and (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: $mobileDevice;
$desktopCheck: "(min-device-width: #{$desktopMinW})";
/************************** 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: "#{$screenPortrait} and #{$phoneCheck} and #{$mobileDevice}";
$phoneLandscape: "#{$screenLandscape} and #{$phoneCheck} and #{$mobileDevice}";
$tabletPortrait: "#{$screenPortrait} and #{$tabletPortraitCheck} and #{$mobileDevice}";
$tabletLandscape: "#{$screenLandscape} and #{$tabletLandscapeCheck} and #{$mobileDevice}";
$tabletLandscapeEmu: "#{$screenLandscape} and #{$tabletLandscapeCheck} and #{$mobileDeviceEmu}";
$tabletPortrait: "#{$screenPortrait} and #{$tabletCheck} and #{$mobileDevice}";
$tabletLandscape: "#{$screenLandscape} and #{$tabletCheck} and #{$mobileDevice}";
$desktopPortrait: "screen and #{$desktopPortraitCheck}";
$desktopLandscape: "screen and #{$desktopLandscapeCheck}";
$desktop: "screen and #{$desktopCheck}";
/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */
$proporMenuOnly: 90%;

View File

@ -36,16 +36,23 @@
//@include test();
@include slMenuTransitions;
margin-left: 0 !important;
#content-area {
.holder-object-and-inspector {
@include slMenuTransitions;
left: $bodyMargin;
right: $bodyMargin;
opacity: 1;
}
}
.holder.holder-create-and-search {
right: $bodyMargin !important;
.create-btn-holder {
// Hide the create button by default in mobile;
// This can be overridden by the examples/mobile bundle
display: none;
}
.holder.holder-treeview-elements {
right: $bodyMargin !important;
}
// When the tree is hidden, these are the
// classes used for the left menu and the
@ -55,10 +62,6 @@
// is hidden.
.pane.left.treeview {
@include trans-prop-nice(opacity, 150ms);
//right: 100% !important;
//width: auto !important;
//overflow-y: hidden;
//overflow-x: hidden;
opacity: 0 !important;
}
.pane.right.items {
@ -72,7 +75,6 @@
// causing cut/copy/paste menu to
// not appear. Should me moved in
// future to properly work
//@include user-select(none);
// Sets the left tree menu when the tree is shown.
.pane.left.treeview {
@ -100,7 +102,6 @@
}
.object-browse-bar {
//@include test();
left: 45px !important;
.context-available {
opacity: 1 !important;
@ -148,7 +149,7 @@
.pane.right.items {
left: 0 !important;
@include transform(translateX($proporMenuOnly));
#content-area {
.holder-object-and-inspector {
opacity: 0;
}
}
@ -159,4 +160,4 @@
.desktop-hide {
display: none;
}
}
}

View File

@ -20,11 +20,12 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* REQUIRES /platform/commonUI/general/res/sass/mobile/_constants.scss */
// Phones in any orientation
@mixin phone {
@media #{$phonePortrait},
#{$phoneLandscape},
#{$phoneLandscapeEmu} {
#{$phoneLandscape} {
@content
}
}
@ -38,8 +39,7 @@
// Phones in landscape orientation
@mixin phoneLandscape {
@media #{$phoneLandscape},
#{$phoneLandscapeEmu} {
@media #{$phoneLandscape} {
@content
}
}
@ -47,8 +47,7 @@
// Tablets in any orientation
@mixin tablet {
@media #{$tabletPortrait},
#{$tabletLandscape},
#{$tabletLandscapeEmu} {
#{$tabletLandscape} {
@content
}
}
@ -62,8 +61,7 @@
// Tablets in landscape orientation
@mixin tabletLandscape {
@media #{$tabletLandscape},
#{$tabletLandscapeEmu} {
@media #{$tabletLandscape} {
@content
}
}
@ -72,10 +70,8 @@
@mixin phoneandtablet {
@media #{$phonePortrait},
#{$phoneLandscape},
#{$phoneLandscapeEmu},
#{$tabletPortrait},
#{$tabletLandscape},
#{$tabletLandscapeEmu} {
#{$tabletLandscape} {
@content
}
}
@ -84,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
}
}

View File

@ -22,21 +22,19 @@
// Override the Create menu for mobile
@include phoneandtablet {
.menu-element {
.super-menu {
$d: 250px;
width: $d;
height: $d;
.super-menu {
$d: 250px;
width: $d;
height: $d;
.pane {
&.left {
border-right: none;
padding-right: 0;
width: 100%;
}
&.right {
display: none;
}
.pane {
&.left {
border-right: none;
padding-right: 0;
width: 100%;
}
&.right {
display: none;
}
}
}

View File

@ -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;
}

View File

@ -20,30 +20,22 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.abs.search-holder {
//@include test(#990000);
height: $treeSearchInputBarH;
bottom: 0;
&.active {
height: auto;
bottom: 0;
}
top: 23px;
// Align with the top of the divider bar, below create button
//margin-top: 10px; // CH comment out
z-index:5;
.clear-icon,
.menu-icon {
cursor: pointer;
font-family: symbolsfont;
@include trans-prop-nice((opacity, color), 150ms);
}
.search {
.clear-icon {
// 'x' in circle icon
&:before {
content: '\e607';
}
}
.holder-search {
$iconWidth: 20px;
$leftMargin: 6px;
$rightPadding: 5px;
@include webkitVal(display, flex);
//display: flex;
@include webkitProp(flex-direction, column);
//flex-direction: column;
height: 100%;
.search-bar {
$textInputHeight: 19px; // This is equal to the default value, 19px
@ -52,38 +44,25 @@
font-size: 0.8em;
max-width: 250px;
position: relative;
width: 100%;
.search-input,
.search-icon {
}
.search-input {
height: $treeSearchInputBarH;
line-height: $treeSearchInputBarH;
padding-top: 0;
padding-bottom: 0;
}
.search-icon,
&:before,
.clear-icon,
.menu-icon {
//@include test(#008800);
@include box-sizing(border-box);
color: $colorInputIcon;
height: $iconD; width: $iconD;
height: $iconD;
width: $iconD;
line-height: $iconD;
position: absolute;
text-align: center;
top: $iconEdgeM;
}
.clear-icon,
.menu-icon {
cursor: pointer;
@include transition(color, .25s);
}
.search-input {
position: relative;
width: 100%;
@ -92,31 +71,21 @@
// Make work for mct-control textfield
input {
width: 100%;
width: inherit; // was 100%
}
}
.search-icon {
//color: $colorItemFg;
left: $interiorMarginSm;
transition: visibility .15s, opacity .15s, color .2s;
pointer-events: none;
&.content {
// Make icon invisible whenever there is text input
//visibility: hidden;
//opacity: 0;
}
}
// Make icon invisible when the text input is focused
.search-input:focus + div.search-icon {
//visibility: hidden;
//opacity: 0;
}
&:before {
// Magnify glass icon
content:'\4d';
font-family: symbolsfont;
left: $interiorMarginSm;
@include trans-prop-nice(color, 250ms);
pointer-events: none;
}
// Make icon lighten when hovering over search bar
.search-input:hover + div.search-icon {
&:hover:before {
color: pullForward($colorInputIcon, 10%);
}
@ -124,13 +93,10 @@
right: $iconD + $interiorMargin;
// Icon is visible only when there is text input
visibility: hidden;
visibility: hidden;
opacity: 0;
transition: visibility .15s, opacity .15s, color .2s;
&.content {
visibility: visible;
&.show {
visibility: visible;
opacity: 1;
}
@ -140,6 +106,8 @@
}
.menu-icon {
// 'v' invoke menu icon
&:before { content: '\76'; }
font-size: 0.8em;
padding-right: $iconEdgeM;
right: $iconEdgeM;
@ -152,43 +120,19 @@
.search-menu-holder {
float: right;
//margin-top: $textInputHeight - 2px;
//left: -50px;
left: -20px;
z-index: 1;
z-index: 70;
transition: visibility .05s, opacity .05s;
&.off {
visibility: hidden;
opacity: 0;
}
}
// Hovering reveals menu
.menu-icon:hover + div.search-menu-holder {
visibility: visible;
}
div.search-menu-holder:hover {
visibility: visible;
}
}
.active-filter-display {
//order: 2;
$s: 0.65em;
$p: $interiorMargin;
@include border-radius($basicCr);
@include box-sizing(border-box);
line-height: 130%;
padding: $p 0;
padding-left: $s * 2.25;
font-size: $s;
//background-color: rgba(#000, 0.3);
//border-radius: $basicCr;
margin-top: $interiorMarginSm;
.clear-filters-icon {
color: $colorInputIcon;
@ -198,43 +142,28 @@
left: 1px;
cursor: pointer;
}
// Transition looks weird when the results list has none
//transition: visibility .2s, opacity .2s;
&.off {
visibility: hidden;
opacity: 0;
height: 0;
margin: 0;
padding: 0;
border: 0;
}
}
.search-scroll {
order: 3;
margin-top: 4px;
// Adjustable scrolling size
overflow-y: auto;
top: auto;
height: auto;
max-height: 100%;
position: relative;
.load-icon {
position: relative;
}
.search-results {
@include trans-prop-nice((opacity, visibility), 250ms);
margin-top: $interiorMarginLg; // Always include margin here to fend off the search input
padding-right: $interiorMargin;
.hint {
margin-bottom: $interiorMarginLg;
font-size: 0.65em;
opacity: 0.6;
}
&.active {
visibility: visible;
opacity: 1;
}
.load-more-button {
margin-top: $interiorMargin 0;
font-size: 0.8em;
position: relative;
left: 50%;
margin-left: -45px;
text-align: center;
width: 90px;
@include transform(translateX(-50%));
display: inline-block;
margin-top: $interiorMargin;
padding: 0 $interiorMarginLg;
font-size: 0.75em;
margin-left: 50%;
white-space: nowrap;
}
}

View File

@ -29,11 +29,13 @@
border-color: lighten($bc, 10%);
}
}
>.object-header.abs {
//@include test(red);
.object-top-bar {
font-size: 0.75em;
height: $ohH;
line-height: $ohH;
.left {
padding-right: $interiorMarginLg;
}
}
>.object-holder.abs {
top: $ohH + $interiorMargin;

View File

@ -31,7 +31,7 @@
}
.contents {
$myM: 0; //$interiorMargin;
$myM: 0;
box-sizing: border-box;
position: absolute;
top: $myM;
@ -46,37 +46,6 @@
}
}
.bar {
.icon.major {
margin-right: $interiorMargin;
}
&.abs {
text-wrap: none;
white-space: nowrap;
&.left,
.left {
width: 45%;
right: auto;
}
&.right,
.right {
width: 45%;
left: auto;
text-align: right;
.icon.major {
margin-left: $interiorMargin * 3;
}
}
.l-flex,
&.l-flex {
.left,
.right {
width: auto;
}
}
}
}
.user-environ {
.browse-area,
.editor {
@ -117,12 +86,10 @@
background: $colorFooterBg;
color: lighten($colorBodyBg, 30%);
font-size: .7rem;
.status-holder {
@include box-sizing(border-box);
@include absPosDefault($interiorMargin);
@include ellipsize();
//line-height: $ueFooterH - ($interiorMargin * 2);
right: 120px;
text-transform: uppercase;
z-index: 1;
@ -141,25 +108,6 @@
}
}
.browse-mode {
.split-layout {
.split-pane-component.pane {
//@include test(green);
&.treeview.left {
min-width: 150px;
max-width: 800px;
width: $ueBrowseLeftPaneTreeW;
}
&.t-inspect.right {
min-width: 200px;
max-width: 600px;
//padding-left: $ueCollapsedPaneEdgeM; // Allow room for mini-tab element
width: $ueBrowseRightPaneInspectW;
}
}
}
}
.edit-mode {
.split-layout {
.split-pane-component.pane.right {
@ -180,7 +128,6 @@
text-transform: uppercase;
height: $ueTopBarH;
line-height: $ueTopBarH;
margin-bottom: $interiorMargin;
}
.primary-pane {
@ -188,30 +135,7 @@
z-index: 2;
}
&.treeview.left {
//.create-btn-holder {
// //bottom: auto;
// //top: 0;
// height: $ueTopBarH;
// .wrapper.menu-element {
// position: absolute;
// bottom: $interiorMargin;
// }
//}
.holder-create-and-search{
}
.search-holder {
top: $ueTopBarH + $interiorMarginLg;
}
.tree-holder {
overflow: auto;
top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin;
}
}
.mini-tab-icon.toggle-pane {
//@include test(blue, 0.3);
z-index: 5;
@include desktop {
$d: $uePaneMiniTabH;
@ -256,7 +180,6 @@
}
}
}
&.items {
.object-browse-bar {
.left.abs,
@ -268,32 +191,8 @@
}
.split-layout {
&.horizontal {
// Slides up and down
> .pane {
// @include test();
margin-top: $interiorMargin;
&:first-child {
margin-top: 0;
}
}
}
/* &.vertical {
// Slides left and right
> .pane.left {
> .holder {
left: $bodyMargin;
}
}
> .pane.right {
> .holder {
right: $bodyMargin;
}
}
}*/
// Specific elements margins
.holder.holder-create-and-search {
.holder.holder-treeview-elements {
top: $bodyMargin;
right: 0;
bottom: $bodyMargin;
@ -314,7 +213,6 @@
bottom: $bodyMargin;
left: $bodyMargin;
right: $bodyMargin;
}
}
}
@ -347,19 +245,17 @@
}
}
/***************************************************** OBJECT BROWSE BAR */
.object-browse-bar {
//@include test(blue);
@include absPosDefault(0, hidden);
@include absPosDefault(0, visible); // Must use visible to avoid hiding view switcher menu
@include box-sizing(border-box);
height: $ueTopBarH;
line-height: $ueTopBarH;
white-space: nowrap;
.left {
padding-right: $interiorMarginLg * 2;
padding-right: $interiorMarginLg;
.l-back {
display: inline-block;
float: left;
margin-right: $interiorMarginLg;
}
}
@ -370,24 +266,22 @@
// right representation.
.pane-tree-hidden {
// Sets the left tree menu when the tree is hidden.
//.pane.left.treeview,
.tree-holder,
.splitter-treeview,
.holder-create-and-search {
.holder-treeview-elements {
opacity: 0;
}
}
.pane-tree-showing {
// Sets the left tree menu when the tree is shown.
//.pane.left.treeview,
.tree-holder,
.splitter-treeview {
@include trans-prop-nice(opacity, $dur: 250ms, $delay: 250ms);
opacity: 1;
}
.holder-create-and-search {
.holder-treeview-elements {
@include trans-prop-nice(opacity, $dur: 250ms, $delay: 200ms);
}
}
@ -401,8 +295,12 @@
}
}
}
.pane-inspect-hidden {
.l-object-and-inspector {
.t-inspect {
z-index: 1 !important; // Move down so that primary pane elements are clickable
}
.l-inspect,
.splitter-inspect {
opacity: 0;
@ -411,6 +309,24 @@
}
@include desktop {
.holder-all {
min-width: $ueDesktopMinW;
}
.split-layout {
.split-pane-component.pane {
&.treeview.left {
min-width: $ueBrowseLeftPaneTreeMinW;
max-width: $ueBrowseLeftPaneTreeMaxW;
width: $ueBrowseLeftPaneTreeW;
}
&.t-inspect.right {
min-width: $ueBrowseRightPaneInspectMinW;
max-width: $ueBrowseRightPaneInspectMaxW;
width: $ueBrowseRightPaneInspectW;
z-index: 3; // Must lift up beyond primary pane to allow overflow to go underneath
}
}
}
.pane.treeview.left .tree-holder {
padding-right: $interiorMargin;
}
@ -420,8 +336,10 @@
.pane-inspect-hidden .l-object-and-inspector {
.pane.left { right: $ueCollapsedPaneEdgeM !important; }
}
.pane:not(.resizing) {
@include trans-prop-nice-resize-w(250ms);
}
.pane.primary-pane .object-browse-bar {
min-width: 200px; // Needed for nice display when primary pane is constrained severely via splitters
}
}

View File

@ -6,10 +6,10 @@
</input>
<a class="ui-symbol icon icon-calendar"
ng-if="structure.format === 'utc' || !structure.format"
ng-click="pickerActive = !pickerActive">
ng-click="picker.active = !picker.active">
</a>
<mct-popup ng-if="pickerActive">
<div mct-click-elsewhere="pickerActive = false">
<mct-popup ng-if="picker.active">
<div mct-click-elsewhere="picker.active = false">
<mct-control key="'datetime-picker'"
ng-model="ngModel"
field="field"

View File

@ -69,9 +69,12 @@ define(
updateFromModel($scope.ngModel[$scope.field]);
}
$scope.picker = { active: false };
$scope.$watch('structure.format', setFormat);
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watch('textValue', updateFromView);
}
return DateTimeFieldController;

View File

@ -51,6 +51,10 @@ define(
position = [ rect.left, rect.top ],
popup = popupService.display(div, position);
// TODO: Handle in CSS;
// https://github.com/nasa/openmctweb/issues/298
div.css('z-index', 75);
transclude(function (clone) {
div.append(clone);
});

View File

@ -91,6 +91,10 @@ define(
expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
});
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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -72,8 +72,6 @@ $colorInputBg: $colorGenBg;
$colorInputFg: $colorBodyFg;
$colorFormText: pushBack($colorBodyFg, 10%);
$colorInputIcon: pushBack($colorBodyFg, 25%);
$colorSelectBg: #ddd;
$colorSelectFg: $colorBodyFg;
// Inspector
$colorInspectorBg: pullForward($colorBodyBg, 5%);
@ -83,6 +81,22 @@ $colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
$colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%);
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
// Status colors, mainly used for messaging and item ancillary symbols
$colorStatusFg: #fff;
$colorStatusDefault: #ccc;
$colorStatusInfo: #60ba7b;
$colorStatusAlert: #ffb66c;
$colorStatusError: #c96b68;
$colorProgressBarOuter: rgba(#000, 0.1);
$colorProgressBarAmt: #0a0;
$progressBarHOverlay: 15px;
$progressBarStripeW: 20px;
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
// Selects
$colorSelectBg: #ddd;
$colorSelectFg: $colorBodyFg;
// Limits and staleness colors//
$colorTelemFresh: pullForward($colorBodyFg, 20%);
$colorTelemStale: pushBack($colorBodyFg, 20%);

View File

@ -96,7 +96,7 @@
"provides": "actionService",
"type": "provider",
"implementation": "actions/ActionProvider.js",
"depends": [ "actions[]" ]
"depends": [ "actions[]", "$log" ]
},
{
"provides": "actionService",
@ -222,7 +222,8 @@
},
{
"key": "topic",
"implementation": "services/Topic.js"
"implementation": "services/Topic.js",
"depends": [ "$log" ]
},
{
"key": "contextualize",

View File

@ -39,9 +39,11 @@ define(
* @imeplements {ActionService}
* @constructor
*/
function ActionProvider(actions) {
function ActionProvider(actions, $log) {
var self = this;
this.$log = $log;
// Build up look-up tables
this.actions = actions;
this.actionsByKey = {};
@ -74,6 +76,7 @@ define(
var context = (actionContext || {}),
category = context.category,
key = context.key,
$log = this.$log,
candidates;
// Instantiate an action; invokes the constructor and
@ -103,12 +106,31 @@ define(
// appliesTo method of given actions (if defined), and
// instantiate those applicable actions.
function createIfApplicable(actions, context) {
return (actions || []).filter(function (Action) {
return Action.appliesTo ?
Action.appliesTo(context) : true;
}).map(function (Action) {
return instantiateAction(Action, context);
});
function isApplicable(Action) {
return Action.appliesTo ? Action.appliesTo(context) : true;
}
function instantiate(Action) {
try {
return instantiateAction(Action, context);
} catch (e) {
$log.error([
"Could not instantiate action",
Action.key,
"due to:",
e.message
].join(" "));
return undefined;
}
}
function isDefined(action) {
return action !== undefined;
}
return (actions || []).filter(isApplicable)
.map(instantiate)
.filter(isDefined);
}
// Match actions to the provided context by comparing "key"

View File

@ -26,6 +26,8 @@ define(
function () {
"use strict";
var ERROR_PREFIX = "Error when notifying listener: ";
/**
* The `topic` service provides a way to create both named,
* shared listeners and anonymous, private listeners.
@ -46,7 +48,7 @@ define(
* @returns {Function}
* @memberof platform/core
*/
function Topic() {
function Topic($log) {
var topics = {};
function createTopic() {
@ -63,7 +65,11 @@ define(
},
notify: function (message) {
listeners.forEach(function (listener) {
listener(message);
try {
listener(message);
} catch (e) {
$log.error(ERROR_PREFIX + e.message);
}
});
}
};

View File

@ -156,8 +156,15 @@ define(
});
};
/**
* Returns the default model for an object of this type. Note that
* this method returns a clone of the original model, so if using this
* method heavily, consider caching the result to optimize performance.
*
* @return {object} The default model for an object of this type.
*/
TypeImpl.prototype.getInitialModel = function () {
return this.typeDef.model || {};
return JSON.parse(JSON.stringify(this.typeDef.model || {}));
};
TypeImpl.prototype.getDefinition = function () {

View File

@ -30,7 +30,8 @@ define(
"use strict";
describe("The action provider", function () {
var actions,
var mockLog,
actions,
actionProvider;
function SimpleAction() {
@ -62,6 +63,10 @@ define(
MetadataAction.key = "metadata";
beforeEach(function () {
mockLog = jasmine.createSpyObj(
'$log',
['error', 'warn', 'info', 'debug']
);
actions = [
SimpleAction,
CategorizedAction,
@ -137,6 +142,42 @@ define(
expect(provided[0].getMetadata()).toEqual("custom metadata");
});
describe("when actions throw errors during instantiation", function () {
var errorText,
provided;
beforeEach(function () {
errorText = "some error text";
function BadAction() {
throw new Error(errorText);
}
provided = new ActionProvider(
[ SimpleAction, BadAction ],
mockLog
).getActions();
});
it("logs an error", function () {
expect(mockLog.error)
.toHaveBeenCalledWith(jasmine.any(String));
});
it("reports the error's message", function () {
expect(
mockLog.error.mostRecentCall.args[0].indexOf(errorText)
).not.toEqual(-1);
});
it("still provides valid actions", function () {
expect(provided.length).toEqual(1);
expect(provided[0].perform()).toEqual("simple");
});
});
});
}
);
);

View File

@ -28,13 +28,18 @@ define(
describe("The 'topic' service", function () {
var topic,
mockLog,
testMessage,
mockCallback;
beforeEach(function () {
testMessage = { someKey: "some value"};
mockLog = jasmine.createSpyObj(
'$log',
[ 'error', 'warn', 'info', 'debug' ]
);
mockCallback = jasmine.createSpy('callback');
topic = new Topic();
topic = new Topic(mockLog);
});
it("notifies listeners on a topic", function () {
@ -65,6 +70,21 @@ define(
expect(mockCallback).toHaveBeenCalledWith(testMessage);
});
it("is robust against errors thrown by listeners", function () {
var mockBadCallback = jasmine.createSpy("bad-callback"),
t = topic();
mockBadCallback.andCallFake(function () {
throw new Error("I'm afraid I can't do that.");
});
t.listen(mockBadCallback);
t.listen(mockCallback);
t.notify(testMessage);
expect(mockCallback).toHaveBeenCalledWith(testMessage);
});
});
}
);

View File

@ -101,6 +101,12 @@ define(
expect(type.getInitialModel().someKey).toEqual("some value");
});
it("provides a fresh initial model each time", function () {
var model = type.getInitialModel();
model.someKey = "some other value";
expect(type.getInitialModel().someKey).toEqual("some value");
});
it("provides type properties", function () {
expect(type.getProperties().length).toEqual(1);
});

View File

@ -146,6 +146,14 @@ define(
});
};
AbstractComposeAction.appliesTo = function (context) {
var applicableObject =
context.selectedObject || context.domainObject;
return !!(applicableObject &&
applicableObject.hasCapability('context'));
};
return AbstractComposeAction;
}
);

View File

@ -60,6 +60,8 @@ define(
);
}
CopyAction.prototype = Object.create(AbstractComposeAction.prototype);
/**
* Updates user about progress of copy. Should not be invoked by
* client code under any circumstances.
@ -145,6 +147,9 @@ define(
return AbstractComposeAction.prototype.perform.call(this)
.then(success, error, notification);
};
CopyAction.appliesTo = AbstractComposeAction.appliesTo;
return CopyAction;
}
);

View File

@ -35,15 +35,15 @@ define(
* @memberof platform/entanglement
*/
function LinkAction(policyService, locationService, linkService, context) {
return new AbstractComposeAction(
policyService,
locationService,
linkService,
context,
"Link"
AbstractComposeAction.apply(
this,
[policyService, locationService, linkService, context, "Link"]
);
}
LinkAction.prototype = Object.create(AbstractComposeAction.prototype);
LinkAction.appliesTo = AbstractComposeAction.appliesTo;
return LinkAction;
}
);

View File

@ -35,15 +35,15 @@ define(
* @memberof platform/entanglement
*/
function MoveAction(policyService, locationService, moveService, context) {
return new AbstractComposeAction(
policyService,
locationService,
moveService,
context,
"Move"
AbstractComposeAction.apply(
this,
[policyService, locationService, moveService, context, "Move"]
);
}
MoveAction.prototype = Object.create(AbstractComposeAction.prototype);
MoveAction.appliesTo = AbstractComposeAction.appliesTo;
return MoveAction;
}
);

View File

@ -102,6 +102,28 @@ define(
composeService = new MockCopyService();
});
it("are only applicable to domain objects with a context", function () {
var noContextObject = domainObjectFactory({
name: 'selectedObject',
model: { name: 'selectedObject' },
capabilities: {}
});
expect(AbstractComposeAction.appliesTo({
selectedObject: selectedObject
})).toBe(true);
expect(AbstractComposeAction.appliesTo({
domainObject: selectedObject
})).toBe(true);
expect(AbstractComposeAction.appliesTo({
selectedObject: noContextObject
})).toBe(false);
expect(AbstractComposeAction.appliesTo({
domainObject: noContextObject
})).toBe(false);
});
describe("with context from context-action", function () {
beforeEach(function () {

View File

@ -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);
};

View File

@ -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();
});

View File

@ -1,5 +1,4 @@
<div class="t-imagery" ng-controller="ImageryController as imagery">
<!-- Main image -->
<div
class="l-image-main-wrapper"
ng-mouseenter="showLocalControls = true;"
@ -33,8 +32,8 @@
>
</div>
<div class="l-image-main-controlbar l-flex bar">
<div class="left">
<div class="l-image-main-controlbar l-flex-row">
<div class="left flex-elem grows">
<a
class="s-btn show-thumbs sm hidden"
ng-click="showThumbsBubble = (showThumbsBubble)? false:true"
@ -43,7 +42,7 @@
<span class="l-time">{{imagery.getTime()}}</span>
<span class="l-date">{{imagery.getDate()}}</span>
</div>
<div class="right">
<div class="right flex-elem">
<a
class="s-btn pause-play"
ng-click="imagery.paused(!imagery.paused())"

View File

@ -20,12 +20,15 @@
at runtime from the About dialog for additional information.
-->
<div class="frame frame-template abs">
<div class="bar abs l-flex object-header object-top-bar">
<div class="left">
<mct-representation key="'object-header'" mct-object="domainObject">
<div class="abs object-top-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation key="'switcher'"
ng-model="representation"
mct-object="domainObject">

View File

@ -113,7 +113,7 @@ define(
Math.floor(position.x / self.gridSize[0]),
Math.floor(position.y / self.gridSize[1])
],
dimensions: DEFAULT_DIMENSIONS
dimensions: self.defaultDimensions()
};
// Mark change as persistable
if ($scope.commit) {

View File

@ -192,6 +192,29 @@ define(
expect(parseInt(styleB.width, 10)).toBeGreaterThan(63);
expect(parseInt(styleB.width, 10)).toBeGreaterThan(31);
});
it("ensures a minimum frame size on drop", function () {
var style;
// Start with a very small frame size
testModel.layoutGrid = [ 1, 1 ];
mockScope.$watch.calls[0].args[1](testModel.layoutGrid);
// Notify that a drop occurred
testModel.composition.push('d');
mockScope.$on.mostRecentCall.args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
mockScope.$watch.calls[0].args[1](['d']);
style = controller.getFrameStyle("d");
// Resulting size should still be reasonably large pixel-size
expect(parseInt(style.width, 10)).toBeGreaterThan(63);
expect(parseInt(style.height, 10)).toBeGreaterThan(31);
});
});
}
);

View File

@ -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>

View File

@ -78,6 +78,8 @@ define(
cachedObjects = [],
updater,
lastBounds,
lastRange,
lastDomain,
handle;
// Populate the scope with axis information (specifically, options
@ -120,16 +122,16 @@ define(
// Reinstantiate the plot updater (e.g. because we have a
// new subscription.) This will clear the plot.
function recreateUpdater() {
updater = new PlotUpdater(
handle,
($scope.axes[0].active || {}).key,
($scope.axes[1].active || {}).key,
PLOT_FIXED_DURATION
);
self.limitTracker = new PlotLimitTracker(
handle,
($scope.axes[1].active || {}).key
);
var domain = $scope.axes[0].active.key,
range = $scope.axes[1].active.key,
duration = PLOT_FIXED_DURATION;
updater = new PlotUpdater(handle, domain, range, duration);
lastDomain = domain;
lastRange = range;
self.limitTracker = new PlotLimitTracker(handle, range);
// Keep any externally-provided bounds
if (lastBounds) {
setBasePanZoom(lastBounds);
@ -201,22 +203,39 @@ define(
}
}
function requery() {
self.pending = true;
releaseSubscription();
subscribe($scope.domainObject);
}
function updateDomainFormat() {
var domainAxis = $scope.axes[0];
plotTelemetryFormatter
.setDomainFormat(domainAxis.active.format);
}
function domainRequery(newDomain) {
if (newDomain !== lastDomain) {
updateDomainFormat();
requery();
}
}
function rangeRequery(newRange) {
if (newRange !== lastRange) {
requery();
}
}
// Respond to a display bounds change (requery for data)
function changeDisplayBounds(event, bounds) {
var domainAxis = $scope.axes[0];
domainAxis.chooseOption(bounds.domain);
plotTelemetryFormatter
.setDomainFormat(domainAxis.active.format);
self.pending = true;
releaseSubscription();
subscribe($scope.domainObject);
updateDomainFormat();
setBasePanZoom(bounds);
}
function updateDomainFormat(format) {
plotTelemetryFormatter.setDomainFormat(format);
requery();
}
this.modeOptions = new PlotModeOptions([], subPlotFactory);
@ -237,6 +256,10 @@ define(
new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
];
// Watch for changes to the selected axis
$scope.$watch("axes[0].active.key", domainRequery);
$scope.$watch("axes[1].active.key", rangeRequery);
// Subscribe to telemetry when a domain object becomes available
$scope.$watch('domainObject', subscribe);

View File

@ -53,6 +53,14 @@ define(
});
}
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1].apply(null, [value]);
}
});
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
@ -263,6 +271,20 @@ define(
]);
expect(mockHandle.request.calls.length).toEqual(2);
});
it("requeries when user changes domain selection", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockHandle.request.calls.length).toEqual(1);
fireWatch("axes[0].active.key", 'someNewKey');
expect(mockHandle.request.calls.length).toEqual(2);
});
it("requeries when user changes range selection", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockHandle.request.calls.length).toEqual(1);
fireWatch("axes[1].active.key", 'someNewKey');
expect(mockHandle.request.calls.length).toEqual(2);
});
});
}
);

View File

@ -19,40 +19,17 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="search"
ng-controller="SearchController as controller">
<!-- Search bar -->
<div class="search-bar"
ng-controller="ClickAwayController as toggle">
<!-- Input field -->
<div class="l-flex-col flex-elem grows holder holder-search" ng-controller="SearchController as controller">
<div class="search-bar flex-elem" ng-controller="ClickAwayController as toggle">
<input class="search-input"
type="text"
ng-model="ngModel.input"
ng-keyup="controller.search()" />
<!-- Search icon -->
<!-- ui symbols for search are 'd' and 'M' -->
<div class="ui-symbol search-icon"
ng-class="{content: !(ngModel.input === '' || ngModel.input === undefined)}">
M
</div>
<!-- Clear icon/button 'x' -->
<a class="ui-symbol clear-icon"
ng-class="{content: !(ngModel.input === '' || ngModel.input === undefined)}"
ng-click="ngModel.input = ''; controller.search()">
&#xe607;
</a>
<!-- Menu icon/button 'v' -->
<a class="ui-symbol menu-icon"
ng-click="toggle.toggle()">
v
</a>
<!-- Menu -->
<a class="clear-icon"
ng-class="{show: !(ngModel.input === '' || ngModel.input === undefined)}"
ng-click="ngModel.input = ''; controller.search()"></a>
<a class="menu-icon"
ng-click="toggle.toggle()"></a>
<mct-include key="'search-menu'"
class="menu-element search-menu-holder"
ng-class="{off: !toggle.isActive()}"
@ -60,49 +37,20 @@
ng-click="toggle.setState(true)">
</mct-include>
</div>
<!-- Active filter display -->
<div class="active-filter-display"
<div class="active-filter-display flex-elem"
ng-class="{off: ngModel.filtersString === '' || ngModel.filtersString === undefined || !ngModel.search}"
ng-controller="SearchMenuController as menuController">
<a class="ui-symbol clear-filters-icon"
ng-click="ngModel.checkAll = true; menuController.checkAll()">
&#xe607;
</a>
Filtered by: {{ ngModel.filtersString }}
<a class="clear-icon clear-filters-icon"
ng-click="ngModel.checkAll = true; menuController.checkAll()"></a>Filtered by: {{ ngModel.filtersString }}
</div>
<!-- This div exists to determine scroll bar location -->
<div class="search-scroll abs">
<!-- Results list -->
<div class="results">
<mct-representation key="'search-item'"
ng-repeat="result in results"
mct-object="result.object"
ng-model="ngModel">
</mct-representation>
</div>
<!-- Loading icon -->
<div class="load-icon"
ng-class="{loading: loading}"
ng-if="loading">
<span class="icon wait-spinner"></span>
<span class="title-label">Loading...</span>
</div>
<!-- Load more button -->
<div ng-if="controller.areMore()">
<a class="load-more-button s-btn vsm"
ng-click="controller.loadMore()">
More Results
</a>
</div>
<div class="search-results flex-elem grows vscroll"
ng-class="{ off: !(loading || results.length > 0), loading: loading }">
<mct-representation key="'search-item'"
ng-repeat="result in results"
mct-object="result.object"
ng-model="ngModel">
</mct-representation>
<a class="load-more-button s-btn vsm" ng-if="controller.areMore()" ng-click="controller.loadMore()">More Results</a>
</div>
</div>

View File

@ -0,0 +1,2 @@
Facilitates tracking states associated with specific domain
objects.

View File

@ -0,0 +1,23 @@
{
"extensions": {
"representers": [
{
"implementation": "StatusRepresenter.js"
}
],
"capabilities": [
{
"key": "status",
"implementation": "StatusCapability.js",
"depends": [ "statusService" ]
}
],
"services": [
{
"key": "statusService",
"implementation": "StatusService.js",
"depends": [ "topic" ]
}
]
}
}

View File

@ -0,0 +1,100 @@
/*****************************************************************************
* 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';
/**
* The `status` capability can be used to attach information
* about the state of a domain object, expressed as simple
* string flags.
*
* Representations of domain objects will also receive CSS
* classes which reflect their current status.
* (@see platform/status.StatusRepresenter)
*
* @param {platform/status.StatusService} statusService
* the service which will track domain object status
* within the application.
* @param {DomainObject} the domain object whose status will
* be tracked.
* @constructor
* @memberof platform/status
*/
function StatusCapability(statusService, domainObject) {
this.statusService = statusService;
this.domainObject = domainObject;
}
/**
* List all status flags currently set for this domain object.
* @returns {string[]} all current status flags.
*/
StatusCapability.prototype.list = function () {
return this.statusService.listStatuses(this.domainObject.getId());
};
/**
* Check if a status flag is currently set for this domain object.
* @param {string} status the status to get
* @returns {boolean} true if the flag is present, otherwise false
*/
StatusCapability.prototype.get = function (status) {
return this.list().indexOf(status) !== -1;
};
/**
* Set a status flag on this domain object.
* @param {string} status the status to set
* @param {boolean} state true if the domain object should
* possess this status, false if it should not
*/
StatusCapability.prototype.set = function (status, state) {
return this.statusService.setStatus(
this.domainObject.getId(),
status,
state
);
};
/**
* Listen for changes in this domain object's status.
* @param {Function} callback function to invoke on changes;
* called with the new status of the domain object, as an
* array of strings
* @returns {Function} a function which can be used to stop
* listening to status changes for this domain object.
*/
StatusCapability.prototype.listen = function (callback) {
return this.statusService.listen(
this.domainObject.getId(),
callback
);
};
return StatusCapability;
}
);

View File

@ -0,0 +1,26 @@
/*****************************************************************************
* 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({
CSS_CLASS_PREFIX: 's-status-',
TOPIC_PREFIX: 'status:'
});

View File

@ -0,0 +1,96 @@
/*****************************************************************************
* 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(
['./StatusConstants'],
function (StatusConstants) {
'use strict';
var STATUS_CLASS_PREFIX = StatusConstants.CSS_CLASS_PREFIX;
/**
* Adds/removes CSS classes to `mct-representation`s to reflect the
* current status of represented domain objects, as reported by
* their `status` capability.
*
* Statuses are prefixed with `s-status-` to build CSS class names.
* As such, when a domain object has the status "pending", its
* representations will have the CSS class `s-status-pending`.
*
* @param {angular.Scope} scope the representation's scope object
* @param element the representation's jqLite-wrapped DOM element
* @implements {Representer}
* @constructor
* @memberof platform/status
*/
function StatusRepresenter(scope, element) {
this.element = element;
this.lastClasses = [];
}
/**
* Remove any status-related classes from this representation.
* @private
*/
StatusRepresenter.prototype.clearClasses = function () {
var element = this.element;
this.lastClasses.forEach(function (c) {
element.removeClass(c);
});
};
StatusRepresenter.prototype.represent = function (representation, domainObject) {
var self = this,
statusCapability = domainObject.getCapability('status');
function updateStatus(flags) {
var newClasses = flags.map(function (flag) {
return STATUS_CLASS_PREFIX + flag;
});
self.clearClasses();
newClasses.forEach(function (c) {
self.element.addClass(c);
});
self.lastClasses = newClasses;
}
updateStatus(statusCapability.list());
this.unlisten = statusCapability.listen(updateStatus);
};
StatusRepresenter.prototype.destroy = function () {
this.clearClasses();
if (this.unlisten) {
this.unlisten();
this.unlisten = undefined;
}
};
return StatusRepresenter;
}
);

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* 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(
['./StatusConstants'],
function (StatusConstants) {
'use strict';
var STATUS_PREFIX = StatusConstants.TOPIC_PREFIX;
/**
* The `statusService` maintains information about the current
* status of specific domain objects within the system. Status
* is represented as string flags which are present when a
* domain object possesses that status, and false when it does
* not.
*
* @param {platform/core.Topic} topic the `topic` service, used
* to create/use named listeners.
* @constructor
* @memberof platform/status
*/
function StatusService(topic) {
this.statusTable = {};
this.topic = topic;
}
/**
* Get all status flags currently set for a domain object.
* @param {string} id the identifier of the domain object
* @returns {string[]} an array containing all status flags currently
* applicable to the object with this identifier
*/
StatusService.prototype.listStatuses = function (id) {
return this.statusTable[id] || [];
};
/**
* Set a status flag for a domain object.
* @param {string} id the identifier of the domain object
* @param {string} status the status to set
* @param {boolean} state true if the domain object should
* possess this status, false if it should not
*/
StatusService.prototype.setStatus = function (id, status, state) {
this.statusTable[id] = this.statusTable[id] || [];
this.statusTable[id] = this.statusTable[id].filter(function (s) {
return s !== status;
});
if (state) {
this.statusTable[id].push(status);
}
this.topic(STATUS_PREFIX + id).notify(this.statusTable[id]);
};
/**
* Listen for changes in a domain object's status.
* @param {string} id the identifier of the domain object
* @param {Function} callback function to invoke on changes;
* called with the new status of the domain object, as an
* array of strings
* @returns {Function} a function which can be used to stop
* listening to status changes for this domain object.
*/
StatusService.prototype.listen = function (id, callback) {
return this.topic(STATUS_PREFIX + id).listen(callback);
};
return StatusService;
}
);

View 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,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/StatusCapability"],
function (StatusCapability) {
"use strict";
describe("The status capability", function () {
var mockStatusService,
mockDomainObject,
mockUnlisten,
testId,
testStatusFlags,
capability;
beforeEach(function () {
testId = "some-id";
testStatusFlags = [ 'a', 'b', 'c' ];
mockStatusService = jasmine.createSpyObj(
'statusService',
[ 'listen', 'setStatus', 'listStatuses' ]
);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
[ 'getId', 'getCapability', 'getModel' ]
);
mockUnlisten = jasmine.createSpy('unlisten');
mockStatusService.listen.andReturn(mockUnlisten);
mockStatusService.listStatuses.andReturn(testStatusFlags);
mockDomainObject.getId.andReturn(testId);
capability = new StatusCapability(
mockStatusService,
mockDomainObject
);
});
it("sets status with the statusService", function () {
var testStatus = "some-test-status";
capability.set(testStatus, true);
expect(mockStatusService.setStatus)
.toHaveBeenCalledWith(testId, testStatus, true);
capability.set(testStatus, false);
expect(mockStatusService.setStatus)
.toHaveBeenCalledWith(testId, testStatus, false);
});
it("gets status from the statusService", function () {
expect(capability.list()).toBe(testStatusFlags);
});
it("listens to changes from the statusService", function () {
var mockCallback = jasmine.createSpy();
expect(capability.listen(mockCallback))
.toBe(mockUnlisten);
expect(mockStatusService.listen)
.toHaveBeenCalledWith(testId, mockCallback);
});
it("allows statuses to be checked individually", function () {
expect(capability.get('some-unset-status')).toBe(false);
expect(capability.get(testStatusFlags[0])).toBe(true);
});
});
}
);

View File

@ -0,0 +1,118 @@
/*****************************************************************************
* 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/StatusRepresenter", "../src/StatusConstants"],
function (StatusRepresenter, StatusConstants) {
"use strict";
describe("The status representer", function () {
var mockScope,
mockElement,
testRepresentation,
mockDomainObject,
mockStatusCapability,
mockUnlisten,
elementClasses,
testStatusFlags,
representer;
function verifyClasses() {
expect(Object.keys(elementClasses).sort())
.toEqual(testStatusFlags.map(function (s) {
return StatusConstants.CSS_CLASS_PREFIX + s;
}).sort());
}
function updateStatus(newFlags) {
testStatusFlags = newFlags;
mockStatusCapability.get.andReturn(newFlags);
mockStatusCapability.listen.mostRecentCall
.args[0](newFlags);
}
beforeEach(function () {
testStatusFlags = [ 'x', 'y', 'z' ];
mockScope = {};
mockElement = jasmine.createSpyObj('element', [
'addClass',
'removeClass'
]);
testRepresentation = { key: "someKey" };
mockDomainObject = jasmine.createSpyObj(
'domainObject',
[ 'getModel', 'getId', 'getCapability' ]
);
mockStatusCapability = jasmine.createSpyObj(
'status',
[ 'list', 'get', 'set', 'listen' ]
);
mockUnlisten = jasmine.createSpy();
elementClasses = {};
mockElement.addClass.andCallFake(function (c) {
elementClasses[c] = true;
});
mockElement.removeClass.andCallFake(function (c) {
delete elementClasses[c];
});
mockStatusCapability.list.andReturn(testStatusFlags);
mockStatusCapability.listen.andReturn(mockUnlisten);
mockDomainObject.getCapability.andCallFake(function (c) {
return c === 'status' && mockStatusCapability;
});
representer = new StatusRepresenter(mockScope, mockElement);
representer.represent(testRepresentation, mockDomainObject);
});
it("listens for status changes", function () {
expect(mockStatusCapability.listen)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("initially sets classes to reflect status", verifyClasses);
it("changes classes on status change callbacks", function () {
updateStatus(['a', 'x', '123']);
verifyClasses();
});
it("stops listening when destroyed", function () {
expect(mockUnlisten).not.toHaveBeenCalled();
representer.destroy();
expect(mockUnlisten).toHaveBeenCalled();
});
it("removes status classes when destroyed", function () {
expect(elementClasses).not.toEqual({});
representer.destroy();
expect(elementClasses).toEqual({});
});
});
}
);

View File

@ -0,0 +1,94 @@
/*****************************************************************************
* 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/StatusService"],
function (StatusService) {
"use strict";
describe("The status service", function () {
var mockTopic,
mockTopicInstance,
mockUnlisten,
mockCallback,
testId,
testStatus,
statusService;
beforeEach(function () {
testId = "some-domain-object-identifier";
testStatus = "test-status";
mockTopic = jasmine.createSpy('topic');
mockTopicInstance = jasmine.createSpyObj(
'topicInstance',
[ 'notify', 'listen' ]
);
mockUnlisten = jasmine.createSpy('unlisten');
mockCallback = jasmine.createSpy('callback');
mockTopic.andReturn(mockTopicInstance);
mockTopicInstance.listen.andReturn(mockUnlisten);
statusService = new StatusService(mockTopic);
});
it("initially contains no flags for an object", function () {
expect(statusService.listStatuses(testId)).toEqual([]);
});
it("stores and clears status flags", function () {
statusService.setStatus(testId, testStatus, true);
expect(statusService.listStatuses(testId)).toEqual([testStatus]);
statusService.setStatus(testId, testStatus, false);
expect(statusService.listStatuses(testId)).toEqual([]);
});
it("uses topic to listen for changes", function () {
expect(statusService.listen(testId, mockCallback))
.toEqual(mockUnlisten);
expect(mockTopic)
.toHaveBeenCalledWith(jasmine.any(String));
// Just care that the topic was somehow unique to the object
expect(mockTopic.mostRecentCall.args[0].indexOf(testId))
.not.toEqual(-1);
});
it("notifies listeners of changes", function () {
statusService.setStatus(testId, testStatus, true);
expect(mockTopicInstance.notify)
.toHaveBeenCalledWith([ testStatus ]);
statusService.setStatus(testId, testStatus, false);
expect(mockTopicInstance.notify)
.toHaveBeenCalledWith([ ]);
expect(mockTopic)
.toHaveBeenCalledWith(jasmine.any(String));
// Just care that the topic was somehow unique to the object
expect(mockTopic.mostRecentCall.args[0].indexOf(testId))
.not.toEqual(-1);
});
});
}
);

View File

@ -0,0 +1,5 @@
[
"StatusCapability",
"StatusRepresenter",
"StatusService"
]

View File

@ -285,11 +285,17 @@ define(
* domain objects returned by `getTelemetryObjects()`.
*
* @param {DomainObject} domainObject the object of interest
* @param {string} [key] the symbolic identifier of the domain
* to look up; if omitted, the value for this object's
* default domain will be used
* @returns the most recent domain value observed
*/
TelemetrySubscription.prototype.getDomainValue = function (domainObject) {
var id = domainObject.getId();
return (this.latestValues[id] || {}).domain;
TelemetrySubscription.prototype.getDomainValue = function (domainObject, key) {
var id = domainObject.getId(),
latestValue = this.latestValues[id];
return latestValue && (key ?
latestValue.datum[key] :
latestValue.domain);
};
/**
@ -302,11 +308,17 @@ define(
* domain objects returned by `getTelemetryObjects()`.
*
* @param {DomainObject} domainObject the object of interest
* @param {string} [key] the symbolic identifier of the range
* to look up; if omitted, the value for this object's
* default range will be used
* @returns the most recent range value observed
*/
TelemetrySubscription.prototype.getRangeValue = function (domainObject) {
var id = domainObject.getId();
return (this.latestValues[id] || {}).range;
TelemetrySubscription.prototype.getRangeValue = function (domainObject, key) {
var id = domainObject.getId(),
latestValue = this.latestValues[id];
return latestValue && (key ?
latestValue.datum[key] :
latestValue.range);
};
/**