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
67 changed files with 2779 additions and 1718 deletions

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);
};
/**