mirror of
https://github.com/nasa/openmct.git
synced 2025-06-18 23:28:14 +00:00
Merge remote-tracking branch 'origin/open181' into rems_data_merge
This commit is contained in:
15
README.md
15
README.md
@ -103,6 +103,21 @@ This build will:
|
|||||||
|
|
||||||
Run as `mvn clean install`.
|
Run as `mvn clean install`.
|
||||||
|
|
||||||
|
### Building Documentation
|
||||||
|
|
||||||
|
Open MCT Web's documentation is generated by an
|
||||||
|
[npm](https://www.npmjs.com/)-based build:
|
||||||
|
|
||||||
|
* `npm install` _(only needs to run once)_
|
||||||
|
* `npm run docs`
|
||||||
|
|
||||||
|
Documentation will be generated in `target/docs`. Note that diagram
|
||||||
|
generation is dependent on having [Cairo](http://cairographics.org/download/)
|
||||||
|
installed; see
|
||||||
|
[node-canvas](https://github.com/Automattic/node-canvas#installation)'s
|
||||||
|
documentation for help with installation.
|
||||||
|
|
||||||
|
|
||||||
# Glossary
|
# Glossary
|
||||||
|
|
||||||
Certain terms are used throughout Open MCT Web with consistent meanings
|
Certain terms are used throughout Open MCT Web with consistent meanings
|
||||||
|
@ -34,6 +34,10 @@
|
|||||||
{
|
{
|
||||||
"key": "time",
|
"key": "time",
|
||||||
"name": "Time"
|
"name": "Time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "yesterday",
|
||||||
|
"name": "Yesterday"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ranges": [
|
"ranges": [
|
||||||
|
@ -29,23 +29,25 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var firstObservedTime = Math.floor(Date.now() / 1000);
|
var ONE_DAY = 60 * 60 * 24,
|
||||||
|
firstObservedTime = Math.floor(Date.now() / 1000) - ONE_DAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function SinewaveTelemetrySeries(request) {
|
function SinewaveTelemetrySeries(request) {
|
||||||
var latestObservedTime = Math.floor(Date.now() / 1000),
|
var timeOffset = (request.domain === 'yesterday') ? ONE_DAY : 0,
|
||||||
|
latestTime = Math.floor(Date.now() / 1000) - timeOffset,
|
||||||
|
firstTime = firstObservedTime - timeOffset,
|
||||||
endTime = (request.end !== undefined) ?
|
endTime = (request.end !== undefined) ?
|
||||||
Math.floor(request.end / 1000) : latestObservedTime,
|
Math.floor(request.end / 1000) : latestTime,
|
||||||
count =
|
count = Math.min(endTime, latestTime) - firstTime,
|
||||||
Math.min(endTime, latestObservedTime) - firstObservedTime,
|
period = +request.period || 30,
|
||||||
period = request.period || 30,
|
|
||||||
generatorData = {},
|
generatorData = {},
|
||||||
offset = (request.start !== undefined) ?
|
requestStart = (request.start === undefined) ? firstTime :
|
||||||
Math.floor(request.start / 1000) - firstObservedTime :
|
Math.max(Math.floor(request.start / 1000), firstTime),
|
||||||
0;
|
offset = requestStart - firstTime;
|
||||||
|
|
||||||
if (request.size !== undefined) {
|
if (request.size !== undefined) {
|
||||||
offset = Math.max(offset, count - request.size);
|
offset = Math.max(offset, count - request.size);
|
||||||
@ -56,8 +58,8 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
generatorData.getDomainValue = function (i, domain) {
|
generatorData.getDomainValue = function (i, domain) {
|
||||||
return (i + offset) * 1000 +
|
return (i + offset) * 1000 + firstTime * 1000 -
|
||||||
(domain !== 'delta' ? (firstObservedTime * 1000) : 0);
|
(domain === 'yesterday' ? ONE_DAY : 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
generatorData.getRangeValue = function (i, range) {
|
generatorData.getRangeValue = function (i, range) {
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
{
|
{
|
||||||
"implementation": "WatchIndicator.js",
|
"implementation": "WatchIndicator.js",
|
||||||
"depends": ["$interval", "$rootScope"]
|
"depends": ["$interval", "$rootScope"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"implementation": "DigestIndicator.js",
|
||||||
|
"depends": ["$interval", "$rootScope"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
77
example/profiling/src/DigestIndicator.js
Normal file
77
example/profiling/src/DigestIndicator.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the number of digests that have occurred since the
|
||||||
|
* indicator was first instantiated.
|
||||||
|
* @constructor
|
||||||
|
* @param $interval Angular's $interval
|
||||||
|
* @implements {Indicator}
|
||||||
|
*/
|
||||||
|
function DigestIndicator($interval, $rootScope) {
|
||||||
|
var digests = 0,
|
||||||
|
displayed = 0,
|
||||||
|
start = Date.now();
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var secs = (Date.now() - start) / 1000;
|
||||||
|
displayed = Math.round(digests / secs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function increment() {
|
||||||
|
digests += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootScope.$watch(increment);
|
||||||
|
|
||||||
|
// Update state every second
|
||||||
|
$interval(update, 1000);
|
||||||
|
|
||||||
|
// Provide initial state, too
|
||||||
|
update();
|
||||||
|
|
||||||
|
return {
|
||||||
|
getGlyph: function () {
|
||||||
|
return ".";
|
||||||
|
},
|
||||||
|
getGlyphClass: function () {
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
getText: function () {
|
||||||
|
return displayed + " digests/sec";
|
||||||
|
},
|
||||||
|
getDescription: function () {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return DigestIndicator;
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
@ -42,9 +42,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='object-holder abs vscroll'>
|
<mct-representation key="representation.selected.key"
|
||||||
<mct-representation key="representation.selected.key"
|
mct-object="representation.selected.key && domainObject"
|
||||||
mct-object="representation.selected.key && domainObject">
|
class="abs object-holder">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
|
||||||
</span>
|
</span>
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
<mct-split-pane class='contents abs' anchor='left'>
|
<mct-split-pane class='contents abs' anchor='left'>
|
||||||
<div class='split-pane-component treeview pane left'>
|
<div class='split-pane-component treeview pane left'>
|
||||||
<div class="holder abs l-mobile">
|
<div class="holder abs l-mobile">
|
||||||
<mct-representation key="'create-button'" mct-object="navigatedObject">
|
<mct-representation key="'create-button'"
|
||||||
|
mct-object="navigatedObject"
|
||||||
|
mct-device="desktop">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
<div class='holder search-holder abs'
|
<div class='holder search-holder abs'
|
||||||
ng-class="{active: treeModel.search}">
|
ng-class="{active: treeModel.search}">
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="menu-element wrapper" ng-controller="ClickAwayController as createController">
|
<div class="menu-element wrapper" ng-controller="ClickAwayController as createController">
|
||||||
<div class="s-menu major create-btn" ng-click="createController.toggle()">
|
<div class="s-menu-btn major create-btn" ng-click="createController.toggle()">
|
||||||
<span class="ui-symbol icon type-icon">+</span>
|
<span class="ui-symbol icon type-icon">+</span>
|
||||||
<span class="title-label">Create</span>
|
<span class="title-label">Create</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,12 +30,11 @@
|
|||||||
structure="toolbar.structure"
|
structure="toolbar.structure"
|
||||||
ng-model="toolbar.state">
|
ng-model="toolbar.state">
|
||||||
</mct-toolbar>
|
</mct-toolbar>
|
||||||
<div class='holder abs object-holder work-area'>
|
<mct-representation key="representation.selected.key"
|
||||||
<mct-representation key="representation.selected.key"
|
toolbar="toolbar"
|
||||||
toolbar="toolbar"
|
mct-object="representation.selected.key && domainObject"
|
||||||
mct-object="representation.selected.key && domainObject">
|
class="holder abs object-holder work-area">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<mct-splitter></mct-splitter>
|
<mct-splitter></mct-splitter>
|
||||||
<div
|
<div
|
||||||
|
@ -45,6 +45,7 @@ $ueEditToolBarH: 25px;
|
|||||||
$ueBrowseLeftPaneW: 25%;
|
$ueBrowseLeftPaneW: 25%;
|
||||||
$ueEditLeftPaneW: 75%;
|
$ueEditLeftPaneW: 75%;
|
||||||
$treeSearchInputBarH: 25px;
|
$treeSearchInputBarH: 25px;
|
||||||
|
$ueTimeControlH: (33px, 20px, 20px);
|
||||||
// Overlay
|
// Overlay
|
||||||
$ovrTopBarH: 60px;
|
$ovrTopBarH: 60px;
|
||||||
$ovrFooterH: 30px;
|
$ovrFooterH: 30px;
|
||||||
|
@ -40,11 +40,11 @@
|
|||||||
|
|
||||||
/************************** HTML ENTITIES */
|
/************************** HTML ENTITIES */
|
||||||
a {
|
a {
|
||||||
color: #ccc;
|
color: $colorA;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: $colorAHov;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +125,14 @@ mct-container {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scrolling {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscroll {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.no-margin {
|
.no-margin {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-symbol {
|
.ui-symbol {
|
||||||
|
&.type-icon {
|
||||||
|
color: $colorObjHdrIc;
|
||||||
|
}
|
||||||
&.icon {
|
&.icon {
|
||||||
color: $colorKey;
|
color: $colorKey;
|
||||||
&.alert {
|
&.alert {
|
||||||
@ -41,6 +44,9 @@
|
|||||||
font-size: 1.65em;
|
font-size: 1.65em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.icon-calendar:after {
|
||||||
|
content: "\e605";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar .ui-symbol {
|
.bar .ui-symbol {
|
||||||
@ -52,7 +58,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-menu .invoke-menu,
|
.s-menu-btn .invoke-menu,
|
||||||
.icon.major .invoke-menu {
|
.icon.major .invoke-menu {
|
||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
@ -347,9 +347,10 @@
|
|||||||
/* This doesn't work on an element inside an element with absolute positioning that has height: auto */
|
/* This doesn't work on an element inside an element with absolute positioning that has height: auto */
|
||||||
//position: relative;
|
//position: relative;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
-webkit-transform: translateY(-50%);
|
@include webkitProp(transform, translateY(-50%));
|
||||||
-ms-transform: translateY(-50%);
|
//-webkit-transform: translateY(-50%);
|
||||||
transform: translateY(-50%);
|
//-ms-transform: translateY(-50%);
|
||||||
|
//transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin verticalCenterBlock($holderH, $itemH) {
|
@mixin verticalCenterBlock($holderH, $itemH) {
|
||||||
@ -374,22 +375,8 @@
|
|||||||
overflow-y: $showBar;
|
overflow-y: $showBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin wait-spinner($b: 5px, $c: $colorAlt1) {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
-webkit-animation: rotation .6s infinite linear;
|
|
||||||
-moz-animation: rotation .6s infinite linear;
|
|
||||||
-o-animation: rotation .6s infinite linear;
|
|
||||||
animation: rotation .6s infinite linear;
|
|
||||||
border-color: rgba($c, 0.25);
|
|
||||||
border-top-color: rgba($c, 1.0);
|
|
||||||
border-style: solid;
|
|
||||||
border-width: $b;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin test($c: #ffcc00, $a: 0.2) {
|
@mixin test($c: #ffcc00, $a: 0.2) {
|
||||||
background-color: rgba($c, $a);
|
background-color: rgba($c, $a) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin tmpBorder($c: #ffcc00, $a: 0.75) {
|
@mixin tmpBorder($c: #ffcc00, $a: 0.75) {
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
&.fixed {
|
&.fixed {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
&.scrolling {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.controls,
|
.controls,
|
||||||
label,
|
label,
|
||||||
.inline-block {
|
.inline-block {
|
||||||
|
@ -205,7 +205,7 @@ label.checkbox.custom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-menu label.checkbox.custom {
|
.s-menu-btn label.checkbox.custom {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,49 +295,155 @@ label.checkbox.custom {
|
|||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
$knobH: 100%; //14px;
|
$knobH: 100%; //14px;
|
||||||
$knobW: 12px;
|
|
||||||
$slotH: 50%;
|
|
||||||
.slot {
|
.slot {
|
||||||
// @include border-radius($basicCr * .75);
|
// @include border-radius($basicCr * .75);
|
||||||
@include sliderTrack();
|
//@include sliderTrack();
|
||||||
height: $slotH;
|
|
||||||
width: auto;
|
width: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: ($knobH - $slotH) / 2;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: auto;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
.knob {
|
.knob {
|
||||||
@include btnSubtle();
|
//@include btnSubtle();
|
||||||
@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid);
|
//@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid);
|
||||||
cursor: ew-resize;
|
@include trans-prop-nice-fade(.25s);
|
||||||
|
background-color: $sliderColorKnob;
|
||||||
|
&:hover {
|
||||||
|
background-color: $sliderColorKnobHov;
|
||||||
|
}
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: $knobH;
|
height: $knobH;
|
||||||
width: $knobW;
|
width: $sliderKnobW;
|
||||||
top: 0;
|
top: 0;
|
||||||
auto: 0;
|
auto: 0;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
left: auto;
|
left: auto;
|
||||||
&:before {
|
}
|
||||||
top: 1px;
|
.knob-l {
|
||||||
bottom: 3px;
|
@include border-left-radius($sliderKnobW);
|
||||||
left: ($knobW / 2) - 1;
|
cursor: w-resize;
|
||||||
}
|
}
|
||||||
|
.knob-r {
|
||||||
|
@include border-right-radius($sliderKnobW);
|
||||||
|
cursor: e-resize;
|
||||||
}
|
}
|
||||||
.range {
|
.range {
|
||||||
background: rgba($colorKey, 0.6);
|
@include trans-prop-nice-fade(.25s);
|
||||||
|
background-color: $sliderColorRange;
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0; //$tbOffset;
|
||||||
right: auto;
|
right: auto;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: auto;
|
left: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba($colorKey, 0.7);
|
background-color: $sliderColorRangeHov;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************** DATETIME PICKER */
|
||||||
|
.l-datetime-picker {
|
||||||
|
$r1H: 15px;
|
||||||
|
@include user-select(none);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: $interiorMarginLg !important;
|
||||||
|
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 {
|
||||||
|
width: $pagerW;
|
||||||
|
@extend .ui-symbol;
|
||||||
|
&.prev {
|
||||||
|
right: auto;
|
||||||
|
&:before {
|
||||||
|
content: "\3c";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.next {
|
||||||
|
left: auto;
|
||||||
|
text-align: right;
|
||||||
|
&:before {
|
||||||
|
content: "\3e";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.val {
|
||||||
|
text-align: center;
|
||||||
|
left: $pagerW + $interiorMargin;
|
||||||
|
right: $pagerW + $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.l-calendar,
|
||||||
|
.l-time-selects {
|
||||||
|
border-top: 1px solid $colorInteriorBorder
|
||||||
|
}
|
||||||
|
.l-time-selects {
|
||||||
|
line-height: $formInputH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************** CALENDAR */
|
||||||
|
.l-calendar {
|
||||||
|
$colorMuted: pushBack($colorMenuFg, 30%);
|
||||||
|
ul.l-cal-row {
|
||||||
|
@include display-flex;
|
||||||
|
@include flex-flow(row nowrap);
|
||||||
|
margin-top: 1px;
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
@include flex(1 0);
|
||||||
|
//@include test();
|
||||||
|
margin-left: 1px;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
text-align: center;
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.l-header li {
|
||||||
|
color: $colorMuted;
|
||||||
|
}
|
||||||
|
&.l-body li {
|
||||||
|
@include trans-prop-nice(background-color, .25s);
|
||||||
|
cursor: pointer;
|
||||||
|
&.in-month {
|
||||||
|
background-color: $colorCalCellInMonthBg;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
color: $colorMuted;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
background: $colorCalCellSelectedBg;
|
||||||
|
color: $colorCalCellSelectedFg;
|
||||||
|
.sub {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: $colorCalCellHovBg;
|
||||||
|
color: $colorCalCellHovFg;
|
||||||
|
.sub {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/******************************************************** MENU BUTTONS */
|
/******************************************************** MENU BUTTONS */
|
||||||
.s-menu {
|
.s-menu-btn {
|
||||||
// Formerly .btn-menu
|
// Formerly .btn-menu
|
||||||
@extend .s-btn;
|
@extend .s-btn;
|
||||||
span.l-click-area {
|
span.l-click-area {
|
||||||
@ -62,186 +62,192 @@
|
|||||||
|
|
||||||
/******************************************************** MENUS THEMSELVES */
|
/******************************************************** MENUS THEMSELVES */
|
||||||
.menu-element {
|
.menu-element {
|
||||||
$bg: $colorMenuBg;
|
|
||||||
$fg: $colorMenuFg;
|
|
||||||
$ic: $colorMenuIc;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
.menu {
|
}
|
||||||
@include border-radius($basicCr);
|
|
||||||
@include containerSubtle($bg, $fg);
|
|
||||||
@include boxShdw($shdwMenu);
|
|
||||||
@include txtShdw($shdwMenuText);
|
|
||||||
display: block; // set to block via jQuery
|
|
||||||
padding: $interiorMarginSm 0;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
ul {
|
|
||||||
@include menuUlReset();
|
|
||||||
li {
|
|
||||||
@include box-sizing(border-box);
|
|
||||||
border-top: 1px solid lighten($bg, 20%);
|
|
||||||
color: pullForward($bg, 60%);
|
|
||||||
line-height: $menuLineH;
|
|
||||||
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
|
||||||
position: relative;
|
|
||||||
white-space: nowrap;
|
|
||||||
&:first-child {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background: $colorMenuHovBg;
|
|
||||||
color: $colorMenuHovFg;
|
|
||||||
.icon {
|
|
||||||
color: $colorMenuHovIc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.type-icon {
|
|
||||||
left: $interiorMargin * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu,
|
.s-menu {
|
||||||
.context-menu,
|
@include border-radius($basicCr);
|
||||||
.super-menu {
|
@include containerSubtle($colorMenuBg, $colorMenuFg);
|
||||||
pointer-events: auto;
|
@include boxShdw($shdwMenu);
|
||||||
ul li {
|
@include txtShdw($shdwMenuText);
|
||||||
//padding-left: 25px;
|
padding: $interiorMarginSm 0;
|
||||||
a {
|
}
|
||||||
color: $fg;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
color: $ic;
|
|
||||||
}
|
|
||||||
.type-icon {
|
|
||||||
left: $interiorMargin;
|
|
||||||
}
|
|
||||||
&:hover .icon {
|
|
||||||
//color: lighten($ic, 5%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-menu {
|
.menu {
|
||||||
// Used in search dropdown in tree
|
@extend .s-menu;
|
||||||
@extend .context-menu;
|
display: block;
|
||||||
ul li {
|
position: absolute;
|
||||||
padding-left: 50px;
|
z-index: 10;
|
||||||
.checkbox {
|
ul {
|
||||||
$d: 0.7rem;
|
@include menuUlReset();
|
||||||
position: absolute;
|
li {
|
||||||
left: $interiorMargin;
|
|
||||||
top: ($menuLineH - $d) / 1.5;
|
|
||||||
em {
|
|
||||||
height: $d;
|
|
||||||
width: $d;
|
|
||||||
&:before {
|
|
||||||
font-size: 7px !important;// $d/2;
|
|
||||||
height: $d;
|
|
||||||
width: $d;
|
|
||||||
line-height: $d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.type-icon {
|
|
||||||
left: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.super-menu {
|
|
||||||
$w: 500px;
|
|
||||||
$h: $w - 20;
|
|
||||||
$plw: 50%;
|
|
||||||
$prw: 50%;
|
|
||||||
display: block;
|
|
||||||
width: $w;
|
|
||||||
height: $h;
|
|
||||||
.contents {
|
|
||||||
@include absPosDefault($interiorMargin);
|
|
||||||
}
|
|
||||||
.pane {
|
|
||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
&.left {
|
border-top: 1px solid lighten($colorMenuBg, 20%);
|
||||||
//@include test();
|
color: pullForward($colorMenuBg, 60%);
|
||||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
line-height: $menuLineH;
|
||||||
left: 0;
|
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
||||||
padding-right: $interiorMargin;
|
position: relative;
|
||||||
right: auto;
|
white-space: nowrap;
|
||||||
width: $plw;
|
&:first-child {
|
||||||
overflow-x: hidden;
|
border: none;
|
||||||
overflow-y: auto;
|
}
|
||||||
ul {
|
&:hover {
|
||||||
li {
|
background: $colorMenuHovBg;
|
||||||
@include border-radius($controlCr);
|
color: $colorMenuHovFg;
|
||||||
padding-left: 30px;
|
.icon {
|
||||||
border-top: none;
|
color: $colorMenuHovIc;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.right {
|
.type-icon {
|
||||||
//@include test(red);
|
left: $interiorMargin * 2;
|
||||||
left: auto;
|
|
||||||
right: 0;
|
|
||||||
padding: $interiorMargin * 5;
|
|
||||||
width: $prw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.menu-item-description {
|
|
||||||
.desc-area {
|
|
||||||
&.icon {
|
|
||||||
$h: 150px;
|
|
||||||
color: $colorCreateMenuLgIcon;
|
|
||||||
position: relative;
|
|
||||||
font-size: 8em;
|
|
||||||
left: 0;
|
|
||||||
height: $h;
|
|
||||||
line-height: $h;
|
|
||||||
margin-bottom: $interiorMargin * 5;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
&.title {
|
|
||||||
color: $colorCreateMenuText;
|
|
||||||
font-size: 1.2em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
&.description {
|
|
||||||
//color: lighten($bg, 30%);
|
|
||||||
color: $colorCreateMenuText;
|
|
||||||
font-size: 0.8em;
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.context-menu {
|
|
||||||
font-size: 0.80rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-holder {
|
.menu,
|
||||||
pointer-events: none;
|
.context-menu,
|
||||||
|
.super-menu {
|
||||||
|
pointer-events: auto;
|
||||||
|
ul li {
|
||||||
|
//padding-left: 25px;
|
||||||
|
a {
|
||||||
|
color: $colorMenuFg;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
color: $colorMenuIc;
|
||||||
|
}
|
||||||
|
.type-icon {
|
||||||
|
left: $interiorMargin;
|
||||||
|
}
|
||||||
|
&:hover .icon {
|
||||||
|
//color: lighten($colorMenuIc, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-menu {
|
||||||
|
// Used in search dropdown in tree
|
||||||
|
@extend .context-menu;
|
||||||
|
ul li {
|
||||||
|
padding-left: 50px;
|
||||||
|
.checkbox {
|
||||||
|
$d: 0.7rem;
|
||||||
|
position: absolute;
|
||||||
|
left: $interiorMargin;
|
||||||
|
top: ($menuLineH - $d) / 1.5;
|
||||||
|
em {
|
||||||
|
height: $d;
|
||||||
|
width: $d;
|
||||||
|
&:before {
|
||||||
|
font-size: 7px !important;// $d/2;
|
||||||
|
height: $d;
|
||||||
|
width: $d;
|
||||||
|
line-height: $d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.type-icon {
|
||||||
|
left: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.super-menu {
|
||||||
|
$w: 500px;
|
||||||
|
$h: $w - 20;
|
||||||
|
$plw: 50%;
|
||||||
|
$prw: 50%;
|
||||||
|
display: block;
|
||||||
|
width: $w;
|
||||||
|
height: $h;
|
||||||
|
.contents {
|
||||||
|
@include absPosDefault($interiorMargin);
|
||||||
|
}
|
||||||
|
.pane {
|
||||||
|
@include box-sizing(border-box);
|
||||||
|
&.left {
|
||||||
|
//@include test();
|
||||||
|
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||||
|
left: 0;
|
||||||
|
padding-right: $interiorMargin;
|
||||||
|
right: auto;
|
||||||
|
width: $plw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
@include border-radius($controlCr);
|
||||||
|
padding-left: 30px;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.right {
|
||||||
|
//@include test(red);
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
|
padding: $interiorMargin * 5;
|
||||||
|
width: $prw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu-item-description {
|
||||||
|
.desc-area {
|
||||||
|
&.icon {
|
||||||
|
$h: 150px;
|
||||||
|
color: $colorCreateMenuLgIcon;
|
||||||
|
position: relative;
|
||||||
|
font-size: 8em;
|
||||||
|
left: 0;
|
||||||
|
height: $h;
|
||||||
|
line-height: $h;
|
||||||
|
margin-bottom: $interiorMargin * 5;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
&.title {
|
||||||
|
color: $colorCreateMenuText;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
&.description {
|
||||||
|
//color: lighten($colorMenuBg, 30%);
|
||||||
|
color: $colorCreateMenuText;
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.context-menu {
|
||||||
|
font-size: 0.80rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-holder,
|
||||||
|
.menu-holder {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 200px;
|
|
||||||
width: 170px;
|
|
||||||
z-index: 70;
|
z-index: 70;
|
||||||
.context-menu-wrapper {
|
.context-menu-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.context-menu {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&.go-left .context-menu {
|
&.go-left .context-menu,
|
||||||
|
&.go-left .menu {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
&.go-up .context-menu {
|
&.go-up .context-menu,
|
||||||
|
&.go-up .menu {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.context-menu-holder {
|
||||||
|
pointer-events: none;
|
||||||
|
height: 200px;
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-bar.right .menu,
|
.btn-bar.right .menu,
|
||||||
.menus-to-left .menu {
|
.menus-to-left .menu {
|
||||||
left: auto;
|
left: auto;
|
||||||
|
@ -1,72 +1,155 @@
|
|||||||
.l-time-controller {
|
@mixin toiLineHovEffects() {
|
||||||
$inputTxtW: 90px;
|
//@include pulse(.25s);
|
||||||
$knobW: 9px;
|
&:before,
|
||||||
$r1H: 20px;
|
&:after {
|
||||||
$r2H: 30px;
|
background-color: $timeControllerToiLineColorHov;
|
||||||
$r3H: 10px;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
position: relative;
|
.l-time-controller-visible {
|
||||||
margin: $interiorMarginLg 0;
|
|
||||||
min-width: 400px;
|
}
|
||||||
|
|
||||||
|
mct-include.l-time-controller {
|
||||||
|
$minW: 500px;
|
||||||
|
$knobHOffset: 0px;
|
||||||
|
$knobM: ($sliderKnobW + $knobHOffset) * -1;
|
||||||
|
$rangeValPad: $interiorMargin;
|
||||||
|
$rangeValOffset: $sliderKnobW;
|
||||||
|
//$knobCr: $sliderKnobW;
|
||||||
|
$timeRangeSliderLROffset: 130px + $sliderKnobW + $rangeValOffset;
|
||||||
|
$r1H: nth($ueTimeControlH,1);
|
||||||
|
$r2H: nth($ueTimeControlH,2);
|
||||||
|
$r3H: nth($ueTimeControlH,3);
|
||||||
|
|
||||||
|
@include absPosDefault();
|
||||||
|
//@include test();
|
||||||
|
display: block;
|
||||||
|
top: auto;
|
||||||
|
height: $r1H + $r2H + $r3H + ($interiorMargin * 2);
|
||||||
|
min-width: $minW;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
|
||||||
.l-time-range-inputs-holder,
|
.l-time-range-inputs-holder,
|
||||||
.l-time-range-slider {
|
.l-time-range-slider {
|
||||||
font-size: 0.8em;
|
//font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-time-range-inputs-holder,
|
.l-time-range-inputs-holder,
|
||||||
.l-time-range-slider-holder,
|
.l-time-range-slider-holder,
|
||||||
.l-time-range-ticks-holder
|
.l-time-range-ticks-holder
|
||||||
{
|
{
|
||||||
margin-bottom: $interiorMargin;
|
//@include test();
|
||||||
position: relative;
|
@include absPosDefault(0, visible);
|
||||||
|
@include box-sizing(border-box);
|
||||||
|
top: auto;
|
||||||
}
|
}
|
||||||
.l-time-range-slider,
|
.l-time-range-slider,
|
||||||
.l-time-range-ticks {
|
.l-time-range-ticks {
|
||||||
//@include test(red, 0.1);
|
//@include test(red, 0.1);
|
||||||
@include absPosDefault(0, visible);
|
@include absPosDefault(0, visible);
|
||||||
|
left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-time-range-inputs-holder {
|
.l-time-range-inputs-holder {
|
||||||
height: $r1H;
|
//@include test(red);
|
||||||
}
|
height: $r1H; bottom: $r2H + $r3H + ($interiorMarginSm * 2);
|
||||||
|
padding-top: $interiorMargin;
|
||||||
.l-time-range-slider,
|
border-top: 1px solid $colorInteriorBorder;
|
||||||
.l-time-range-ticks {
|
.type-icon {
|
||||||
left: $inputTxtW; right: $inputTxtW;
|
font-size: 120%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.l-time-range-input,
|
||||||
|
.l-time-range-inputs-elem {
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
.lbl {
|
||||||
|
color: $colorPlotLabelFg;
|
||||||
|
}
|
||||||
|
.ui-symbol.icon {
|
||||||
|
font-size: 11px;
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-time-range-slider-holder {
|
.l-time-range-slider-holder {
|
||||||
height: $r2H;
|
//@include test(green);
|
||||||
|
height: $r2H; bottom: $r3H + ($interiorMarginSm * 1);
|
||||||
.range-holder {
|
.range-holder {
|
||||||
@include box-shadow(none);
|
@include box-shadow(none);
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
height: 75%;
|
.range {
|
||||||
|
.toi-line {
|
||||||
|
$myC: $timeControllerToiLineColor;
|
||||||
|
$myW: 8px;
|
||||||
|
@include transform(translateX(50%));
|
||||||
|
position: absolute;
|
||||||
|
//@include test();
|
||||||
|
top: 0; right: 0; bottom: 0px; left: auto;
|
||||||
|
width: $myW;
|
||||||
|
height: auto;
|
||||||
|
z-index: 2;
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
background-color: $myC;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
// Vert line
|
||||||
|
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
|
||||||
|
width: 2px;
|
||||||
|
//top: 0; right: 3px; bottom: 0; left: 3px;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
// Circle element
|
||||||
|
@include border-radius($myW);
|
||||||
|
@include transform(translateY(-50%));
|
||||||
|
top: 50%; right: 0; bottom: auto; left: 0;
|
||||||
|
width: auto;
|
||||||
|
height: $myW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover .toi-line {
|
||||||
|
@include toiLineHovEffects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(:active) {
|
||||||
|
//@include test(#ff00cc);
|
||||||
|
.knob,
|
||||||
|
.range {
|
||||||
|
@include transition-property(left, right);
|
||||||
|
@include transition-duration(500ms);
|
||||||
|
@include transition-timing-function(ease-in-out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-time-range-ticks-holder {
|
.l-time-range-ticks-holder {
|
||||||
height: $r3H;
|
height: $r3H;
|
||||||
.l-time-range-ticks {
|
.l-time-range-ticks {
|
||||||
border-top: 1px solid $colorInteriorBorder;
|
border-top: 1px solid $colorTick;
|
||||||
.tick {
|
.tick {
|
||||||
background-color: $colorInteriorBorder;
|
background-color: $colorTick;
|
||||||
border:none;
|
border:none;
|
||||||
|
height: 5px;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
|
position: absolute;
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.l-time-range-tick-label {
|
.l-time-range-tick-label {
|
||||||
color: lighten($colorInteriorBorder, 20%);
|
@include webkitProp(transform, translateX(-50%));
|
||||||
font-size: 0.7em;
|
color: $colorPlotLabelFg;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.9em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-left: -0.5 * $tickLblW;
|
top: 8px;
|
||||||
text-align: center;
|
white-space: nowrap;
|
||||||
top: $r3H;
|
|
||||||
width: $tickLblW;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,31 +157,47 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.knob {
|
.knob {
|
||||||
width: $knobW;
|
z-index: 2;
|
||||||
.range-value {
|
.range-value {
|
||||||
$w: 75px;
|
//@include test($sliderColorRange);
|
||||||
//@include test();
|
@include trans-prop-nice-fade(.25s);
|
||||||
|
padding: 0 $rangeValOffset;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
height: $r2H;
|
||||||
margin-top: -7px; // Label is 13px high
|
line-height: $r2H;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: $w;
|
|
||||||
}
|
}
|
||||||
&:hover .range-value {
|
&:hover .range-value {
|
||||||
color: $colorKey;
|
color: $sliderColorKnobHov;
|
||||||
}
|
}
|
||||||
&.knob-l {
|
&.knob-l {
|
||||||
margin-left: $knobW / -2;
|
//@include border-bottom-left-radius($knobCr); // MOVED TO _CONTROLS.SCSS
|
||||||
|
margin-left: $knobM;
|
||||||
.range-value {
|
.range-value {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
right: $knobW + $interiorMargin;
|
right: $rangeValOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.knob-r {
|
&.knob-r {
|
||||||
margin-right: $knobW / -2;
|
//@include border-bottom-right-radius($knobCr);
|
||||||
|
margin-right: $knobM;
|
||||||
.range-value {
|
.range-value {
|
||||||
left: $knobW + $interiorMargin;
|
left: $rangeValOffset;
|
||||||
|
}
|
||||||
|
&:hover + .range-holder .range .toi-line {
|
||||||
|
@include toiLineHovEffects;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//.slot.range-holder {
|
||||||
|
// background-color: $sliderColorRangeHolder;
|
||||||
|
//}
|
||||||
|
|
||||||
|
.s-time-range-val {
|
||||||
|
//@include test();
|
||||||
|
@include border-radius($controlCr);
|
||||||
|
background-color: $colorInputBg;
|
||||||
|
padding: 1px 1px 0 $interiorMargin;
|
||||||
|
}
|
@ -19,19 +19,29 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@mixin complexFieldHolder($myW) {
|
||||||
|
width: $myW + $interiorMargin;
|
||||||
|
input[type="text"] {
|
||||||
|
width: $myW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.complex.datetime {
|
.complex.datetime {
|
||||||
span {
|
span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
.field-hints,
|
.field-hints,
|
||||||
.fields {
|
.fields {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.field-hints {
|
.field-hints {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
.fields {
|
.fields {
|
||||||
margin-top: $interiorMarginSm 0;
|
margin-top: $interiorMarginSm 0;
|
||||||
@ -39,19 +49,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
$myW: 80px;
|
@include complexFieldHolder(80px);
|
||||||
width: $myW + $interiorMargin;
|
}
|
||||||
input {
|
|
||||||
width: $myW;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.time.md {
|
||||||
|
@include complexFieldHolder(60px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.time.sm {
|
.time.sm {
|
||||||
$myW: 40px;
|
@include complexFieldHolder(40px);
|
||||||
width: $myW + $interiorMargin;
|
|
||||||
input {
|
|
||||||
width: $myW;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,10 +21,13 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
.select {
|
.select {
|
||||||
@include btnSubtle($colorSelectBg);
|
@include btnSubtle($colorSelectBg);
|
||||||
margin: 0 0 2px 2px; // Needed to avoid dropshadow from being clipped by parent containers
|
@if $shdwBtns != none {
|
||||||
|
margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
|
||||||
|
}
|
||||||
padding: 0 $interiorMargin;
|
padding: 0 $interiorMargin;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
line-height: $formInputH;
|
||||||
select {
|
select {
|
||||||
@include appearance(none);
|
@include appearance(none);
|
||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
@ -40,11 +43,8 @@
|
|||||||
}
|
}
|
||||||
&:after {
|
&:after {
|
||||||
@include contextArrow();
|
@include contextArrow();
|
||||||
|
pointer-events: none;
|
||||||
color: rgba($colorSelectFg, percentToDecimal($contrastInvokeMenuPercent));
|
color: rgba($colorSelectFg, percentToDecimal($contrastInvokeMenuPercent));
|
||||||
//content:"v";
|
|
||||||
//display: block;
|
|
||||||
//font-family: 'symbolsfont';
|
|
||||||
//pointer-events: none;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: $interiorMargin; top: 0;
|
right: $interiorMargin; top: 0;
|
||||||
}
|
}
|
||||||
|
@ -19,24 +19,45 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@-webkit-keyframes rotation {
|
@include keyframes(rotation) {
|
||||||
from {-webkit-transform: rotate(0deg);}
|
0% { transform: rotate(0deg); }
|
||||||
to {-webkit-transform: rotate(359deg);}
|
100% { transform: rotate(359deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@-moz-keyframes rotation {
|
@mixin wait-spinner2($b: 5px, $c: $colorAlt1) {
|
||||||
from {-moz-transform: rotate(0deg);}
|
@include keyframes(rotateCentered) {
|
||||||
to {-moz-transform: rotate(359deg);}
|
0% { transform: translateX(-50%) translateY(-50%) rotate(0deg); }
|
||||||
|
100% { transform: translateX(-50%) translateY(-50%) rotate(359deg); }
|
||||||
|
}
|
||||||
|
@include animation-name(rotateCentered);
|
||||||
|
@include animation-duration(0.5s);
|
||||||
|
@include animation-iteration-count(infinite);
|
||||||
|
@include animation-timing-function(linear);
|
||||||
|
border-color: rgba($c, 0.25);
|
||||||
|
border-top-color: rgba($c, 1.0);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px;
|
||||||
|
@include border-radius(100%);
|
||||||
|
@include box-sizing(border-box);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 0; width: 0;
|
||||||
|
padding: 7%;
|
||||||
|
left: 50%; top: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-o-keyframes rotation {
|
@mixin wait-spinner($b: 5px, $c: $colorAlt1) {
|
||||||
from {-o-transform: rotate(0deg);}
|
display: block;
|
||||||
to {-o-transform: rotate(359deg);}
|
position: absolute;
|
||||||
}
|
-webkit-animation: rotation .6s infinite linear;
|
||||||
|
-moz-animation: rotation .6s infinite linear;
|
||||||
@keyframes rotation {
|
-o-animation: rotation .6s infinite linear;
|
||||||
from {transform: rotate(0deg);}
|
animation: rotation .6s infinite linear;
|
||||||
to {transform: rotate(359deg);}
|
border-color: rgba($c, 0.25);
|
||||||
|
border-top-color: rgba($c, 1.0);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: $b;
|
||||||
|
@include border-radius(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.t-wait-spinner,
|
.t-wait-spinner,
|
||||||
@ -97,3 +118,27 @@
|
|||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
top: 0; left: 0;
|
top: 0; left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
// Can be applied to any block element with height and width
|
||||||
|
pointer-events: none;
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
@include wait-spinner2(5px, $colorLoadingFg);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
@include absPosDefault();
|
||||||
|
background: $colorLoadingBg;
|
||||||
|
display: block;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
&.tree-item:before {
|
||||||
|
padding: $menuLineH / 4;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -40,6 +40,11 @@ table {
|
|||||||
thead, .thead {
|
thead, .thead {
|
||||||
border-bottom: 1px solid $colorTabHeaderBorder;
|
border-bottom: 1px solid $colorTabHeaderBorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.fixed-header) tr th {
|
||||||
|
background-color: $colorTabHeaderBg;
|
||||||
|
}
|
||||||
|
|
||||||
tbody, .tbody {
|
tbody, .tbody {
|
||||||
display: table-row-group;
|
display: table-row-group;
|
||||||
tr, .tr {
|
tr, .tr {
|
||||||
@ -64,7 +69,6 @@ table {
|
|||||||
display: table-cell;
|
display: table-cell;
|
||||||
}
|
}
|
||||||
th, .th {
|
th, .th {
|
||||||
background-color: $colorTabHeaderBg;
|
|
||||||
border-left: 1px solid $colorTabHeaderBorder;
|
border-left: 1px solid $colorTabHeaderBorder;
|
||||||
color: $colorTabHeaderFg;
|
color: $colorTabHeaderFg;
|
||||||
padding: $tabularTdPadLR $tabularTdPadLR;
|
padding: $tabularTdPadLR $tabularTdPadLR;
|
||||||
@ -143,7 +147,7 @@ table {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: $tabularHeaderH;
|
height: $tabularHeaderH;
|
||||||
background: rgba(#fff, 0.15);
|
background-color: $colorTabHeaderBg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody, .tbody {
|
tbody, .tbody {
|
||||||
|
@ -89,7 +89,7 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
.gl-plot-label,
|
.gl-plot-label,
|
||||||
.l-plot-label {
|
.l-plot-label {
|
||||||
// @include test(yellow);
|
// @include test(yellow);
|
||||||
color: lighten($colorBodyFg, 20%);
|
color: $colorPlotLabelFg;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
// text-transform: uppercase;
|
// text-transform: uppercase;
|
||||||
|
@ -214,8 +214,6 @@
|
|||||||
|
|
||||||
.search-scroll {
|
.search-scroll {
|
||||||
order: 3;
|
order: 3;
|
||||||
|
|
||||||
//padding-right: $rightPadding;
|
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
|
||||||
// Adjustable scrolling size
|
// Adjustable scrolling size
|
||||||
@ -227,28 +225,6 @@
|
|||||||
|
|
||||||
.load-icon {
|
.load-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
&.loading {
|
|
||||||
pointer-events: none;
|
|
||||||
margin-left: $leftMargin;
|
|
||||||
|
|
||||||
.title-label {
|
|
||||||
// Text styling
|
|
||||||
font-style: italic;
|
|
||||||
font-size: .9em;
|
|
||||||
opacity: 0.5;
|
|
||||||
|
|
||||||
// Text positioning
|
|
||||||
margin-left: $iconWidth + $leftMargin;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
.wait-spinner {
|
|
||||||
margin-left: $leftMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.loading) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.load-more-button {
|
.load-more-button {
|
||||||
|
@ -83,7 +83,6 @@ ul.tree {
|
|||||||
.icon {
|
.icon {
|
||||||
&.l-icon-link,
|
&.l-icon-link,
|
||||||
&.l-icon-alert {
|
&.l-icon-alert {
|
||||||
//@include txtShdw($shdwItemTreeIcon);
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
@ -105,26 +104,12 @@ ul.tree {
|
|||||||
@include absPosDefault();
|
@include absPosDefault();
|
||||||
display: block;
|
display: block;
|
||||||
left: $runningItemW + ($interiorMargin * 3);
|
left: $runningItemW + ($interiorMargin * 3);
|
||||||
//right: $treeContextTriggerW + $interiorMargin;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.loading {
|
|
||||||
pointer-events: none;
|
|
||||||
.label {
|
|
||||||
opacity: 0.5;
|
|
||||||
.title-label {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.wait-spinner {
|
|
||||||
margin-left: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
background: $colorItemTreeSelectedBg;
|
background: $colorItemTreeSelectedBg;
|
||||||
color: $colorItemTreeSelectedFg;
|
color: $colorItemTreeSelectedFg;
|
||||||
@ -142,9 +127,6 @@ ul.tree {
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: rgba($colorBodyFg, 0.1); //lighten($colorBodyBg, 5%);
|
background: rgba($colorBodyFg, 0.1); //lighten($colorBodyBg, 5%);
|
||||||
color: pullForward($colorBodyFg, 20%);
|
color: pullForward($colorBodyFg, 20%);
|
||||||
//.context-trigger {
|
|
||||||
// display: block;
|
|
||||||
//}
|
|
||||||
.icon {
|
.icon {
|
||||||
color: $colorItemTreeIconHover;
|
color: $colorItemTreeIconHover;
|
||||||
}
|
}
|
||||||
@ -158,7 +140,6 @@ ul.tree {
|
|||||||
|
|
||||||
.context-trigger {
|
.context-trigger {
|
||||||
$h: 0.9rem;
|
$h: 0.9rem;
|
||||||
//display: none;
|
|
||||||
top: -1px;
|
top: -1px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: $interiorMarginSm;
|
right: $interiorMarginSm;
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
}
|
}
|
||||||
&.frame-template {
|
&.frame-template {
|
||||||
.s-btn,
|
.s-btn,
|
||||||
.s-menu {
|
.s-menu-btn {
|
||||||
height: $ohH;
|
height: $ohH;
|
||||||
line-height: $ohH;
|
line-height: $ohH;
|
||||||
padding: 0 $interiorMargin;
|
padding: 0 $interiorMargin;
|
||||||
@ -56,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-menu:after {
|
.s-menu-btn:after {
|
||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,21 +30,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.holder-all {
|
.holder-all {
|
||||||
$myM: 0; // $interiorMarginSm;
|
$myM: 0; // $interiorMarginSm;
|
||||||
top: $myM;
|
top: $myM;
|
||||||
right: $myM;
|
right: $myM;
|
||||||
bottom: $myM;
|
bottom: $myM;
|
||||||
left: $myM;
|
left: $myM;
|
||||||
}
|
}
|
||||||
|
|
||||||
.browse-area,
|
.browse-area,
|
||||||
.edit-area,
|
.edit-area,
|
||||||
.editor {
|
.editor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
@include border-radius($basicCr * 1.5);
|
@include border-radius($basicCr * 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contents {
|
.contents {
|
||||||
@ -68,8 +68,8 @@
|
|||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
}
|
}
|
||||||
&.abs {
|
&.abs {
|
||||||
text-wrap: none;
|
text-wrap: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
&.left,
|
&.left,
|
||||||
.left {
|
.left {
|
||||||
width: 45%;
|
width: 45%;
|
||||||
@ -95,51 +95,51 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-environ {
|
.user-environ {
|
||||||
.browse-area,
|
.browse-area,
|
||||||
.edit-area,
|
.edit-area,
|
||||||
.editor {
|
.editor {
|
||||||
top: $bodyMargin + $ueTopBarH + ($interiorMargin);
|
top: $bodyMargin + $ueTopBarH + ($interiorMargin);
|
||||||
right: $bodyMargin;
|
right: $bodyMargin;
|
||||||
bottom: $ueFooterH + $bodyMargin;
|
bottom: $ueFooterH + $bodyMargin;
|
||||||
left: $bodyMargin;
|
left: $bodyMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
.browse-area,
|
.browse-area,
|
||||||
.edit-area {
|
.edit-area {
|
||||||
> .contents {
|
> .contents {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-area {
|
.edit-area {
|
||||||
$tbH: $btnToolbarH + $interiorMargin;
|
$tbH: $btnToolbarH + $interiorMargin;
|
||||||
top: $bodyMargin + $ueTopBarEditH + ($interiorMargin);
|
top: $bodyMargin + $ueTopBarEditH + ($interiorMargin);
|
||||||
.tool-bar {
|
.tool-bar {
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
height: $tbH;
|
height: $tbH;
|
||||||
line-height: $btnToolbarH;
|
line-height: $btnToolbarH;
|
||||||
}
|
}
|
||||||
.work-area {
|
.work-area {
|
||||||
top: $tbH + $interiorMargin * 2;
|
top: $tbH + $interiorMargin * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ue-bottom-bar {
|
.ue-bottom-bar {
|
||||||
//@include absPosDefault($bodyMargin);
|
//@include absPosDefault($bodyMargin);
|
||||||
@include absPosDefault(0);// New status bar design
|
@include absPosDefault(0); // New status bar design
|
||||||
top: auto;
|
top: auto;
|
||||||
height: $ueFooterH;
|
height: $ueFooterH;
|
||||||
.status-holder {
|
.status-holder {
|
||||||
//right: $ueAppLogoW + $bodyMargin; New status bar design
|
//right: $ueAppLogoW + $bodyMargin; New status bar design
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.app-logo {
|
.app-logo {
|
||||||
left: auto;
|
left: auto;
|
||||||
width: $ueAppLogoW;
|
width: $ueAppLogoW;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cols {
|
.cols {
|
||||||
@ -225,89 +225,89 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.pane {
|
.pane {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
&.treeview.left {
|
&.treeview.left {
|
||||||
.create-btn-holder {
|
.create-btn-holder {
|
||||||
bottom: auto; top: 0;
|
bottom: auto;
|
||||||
height: $ueTopBarH;
|
top: 0;
|
||||||
.wrapper.menu-element {
|
height: $ueTopBarH;
|
||||||
position: absolute;
|
.wrapper.menu-element {
|
||||||
bottom: $interiorMargin;
|
position: absolute;
|
||||||
}
|
bottom: $interiorMargin;
|
||||||
}
|
}
|
||||||
.search-holder {
|
}
|
||||||
top: $ueTopBarH + $interiorMarginLg;
|
.search-holder {
|
||||||
}
|
top: $ueTopBarH + $interiorMarginLg;
|
||||||
.tree-holder {
|
}
|
||||||
overflow: auto;
|
.tree-holder {
|
||||||
top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin;
|
overflow: auto;
|
||||||
}
|
top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.items {
|
&.items {
|
||||||
.object-browse-bar {
|
.object-browse-bar {
|
||||||
.left.abs,
|
.left.abs,
|
||||||
.right.abs {
|
.right.abs {
|
||||||
top: auto;
|
top: auto;
|
||||||
}
|
}
|
||||||
//.left.abs {
|
|
||||||
// @include tmpBorder(green);
|
|
||||||
//}
|
|
||||||
//.right.abs {
|
|
||||||
// @include tmpBorder(red);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
.object-holder {
|
|
||||||
top: $ueTopBarH + $interiorMarginLg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.object-holder {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.split-layout {
|
.split-layout {
|
||||||
&.horizontal {
|
&.horizontal {
|
||||||
// Slides up and down
|
// Slides up and down
|
||||||
>.pane {
|
> .pane {
|
||||||
// @include test();
|
// @include test();
|
||||||
margin-top: $interiorMargin;
|
margin-top: $interiorMargin;
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.vertical {
|
&.vertical {
|
||||||
// Slides left and right
|
// Slides left and right
|
||||||
>.pane {
|
> .pane {
|
||||||
// @include test();
|
// @include test();
|
||||||
margin-left: $interiorMargin;
|
margin-left: $interiorMargin;
|
||||||
>.holder {
|
> .holder {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
.holder {
|
.holder {
|
||||||
right: $interiorMarginSm;
|
right: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-holder {
|
||||||
|
overflow: hidden; // Contained objects need to handle their own overflow now
|
||||||
|
top: $ueTopBarH + $interiorMarginLg;
|
||||||
|
> ng-include {
|
||||||
|
@include absPosDefault(0, auto);
|
||||||
|
}
|
||||||
|
&.l-controls-visible {
|
||||||
|
&.l-time-controller-visible {
|
||||||
|
bottom: nth($ueTimeControlH,1) + nth($ueTimeControlH,2) +nth($ueTimeControlH,3) + ($interiorMargin * 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar .s-btn,
|
.object-browse-bar .s-btn,
|
||||||
.top-bar .buttons-main .s-btn,
|
.top-bar .buttons-main .s-btn,
|
||||||
.top-bar .s-menu,
|
.top-bar .s-menu-btn,
|
||||||
.tool-bar .s-btn,
|
.tool-bar .s-btn,
|
||||||
.tool-bar .s-menu {
|
.tool-bar .s-menu-btn {
|
||||||
$h: $btnToolbarH;
|
$h: $btnToolbarH;
|
||||||
height: $h;
|
height: $h;
|
||||||
line-height: $h;
|
line-height: $h;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar,
|
.object-browse-bar,
|
||||||
@ -318,33 +318,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar {
|
.object-browse-bar {
|
||||||
//@include test(blue);
|
//@include test(blue);
|
||||||
@include absPosDefault(0, visible);
|
@include absPosDefault(0, visible);
|
||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
height: $ueTopBarH;
|
height: $ueTopBarH;
|
||||||
line-height: $ueTopBarH;
|
line-height: $ueTopBarH;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
padding-right: $interiorMarginLg * 2;
|
padding-right: $interiorMarginLg * 2;
|
||||||
.l-back {
|
.l-back {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: $interiorMarginLg;
|
margin-right: $interiorMarginLg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-flex {
|
.l-flex {
|
||||||
@include webkitVal('display', 'flex');
|
@include webkitVal('display', 'flex');
|
||||||
@include webkitProp('flex-flow', 'row nowrap');
|
@include webkitProp('flex-flow', 'row nowrap');
|
||||||
.left {
|
.left {
|
||||||
//@include test(red);
|
//@include test(red);
|
||||||
@include webkitProp(flex, '1 1 0');
|
@include webkitProp(flex, '1 1 0');
|
||||||
padding-right: $interiorMarginLg;
|
padding-right: $interiorMarginLg;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.vscroll {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
@ -20,42 +20,47 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<div ng-controller="DateTimePickerController">
|
<div ng-controller="DateTimePickerController" class="l-datetime-picker s-datetime-picker s-menu">
|
||||||
<div style="vertical-align: top; display: inline-block">
|
<div class="holder">
|
||||||
<div style="text-align: center;">
|
<div class="l-month-year-pager">
|
||||||
<a ng-click="changeMonth(-1)"><</a>
|
<a class="pager prev" ng-click="changeMonth(-1)"></a>
|
||||||
{{month}} {{year}}
|
<span class="val">{{month}} {{year}}</span>
|
||||||
<a ng-click="changeMonth(1)">></a>
|
<a class="pager next" ng-click="changeMonth(1)"></a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="l-calendar">
|
||||||
<table>
|
<ul class="l-cal-row l-header">
|
||||||
<tr>
|
<li ng-repeat="day in ['Su','Mo','Tu','We','Th','Fr','Sa']">{{day}}</li>
|
||||||
<th ng-repeat="day in ['Su','Mo','Tu','We','Th','Fr','Sa']">
|
</ul>
|
||||||
{{day}}
|
<ul class="l-cal-row l-body" ng-repeat="row in table">
|
||||||
</th>
|
<li ng-repeat="cell in row"
|
||||||
</tr>
|
ng-click="select(cell)"
|
||||||
<tr ng-repeat="row in table">
|
ng-class='{ "in-month": isInCurrentMonth(cell), selected: isSelected(cell) }'>
|
||||||
<td style="text-align: center;"
|
<div class="prime">{{cell.day}}</div>
|
||||||
ng-repeat="cell in row"
|
<div class="sub">{{cell.dayOfYear}}</div>
|
||||||
ng-click="select(cell)"
|
</li>
|
||||||
ng-class='{
|
</ul>
|
||||||
disabled: !isSelectable(cell),
|
</div>
|
||||||
test: isSelected(cell)
|
</div>
|
||||||
}'>
|
<div class="l-time-selects complex datetime"
|
||||||
<div>{{cell.day}}</div>
|
ng-show="options">
|
||||||
<div style="font-size: 80%">{{cell.dayOfYear}}</div>
|
<div class="field-hints">
|
||||||
</td>
|
<span class="hint time md"
|
||||||
</tr>
|
ng-repeat="key in ['hours', 'minutes', 'seconds']"
|
||||||
</table>
|
ng-if="options[key]">
|
||||||
</div>
|
{{nameFor(key)}}
|
||||||
</div>
|
</span>
|
||||||
<div style="vertical-align: top; display: inline-block"
|
</div>
|
||||||
ng-repeat="key in ['hours', 'minutes', 'seconds']"
|
<div>
|
||||||
ng-if="options[key]">
|
<span class="field control time md"
|
||||||
<div>{{nameFor(key)}}</div>
|
ng-repeat="key in ['hours', 'minutes', 'seconds']"
|
||||||
<select size="10"
|
ng-if="options[key]">
|
||||||
ng-model="time[key]"
|
<div class='form-control select'>
|
||||||
ng-options="i for i in optionsFor(key)">
|
<select size="1"
|
||||||
</select>
|
ng-model="time[key]"
|
||||||
</div>
|
ng-options="i for i in optionsFor(key)">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
<span ng-controller="ViewSwitcherController">
|
<span ng-controller="ViewSwitcherController">
|
||||||
<div
|
<div
|
||||||
class="view-switcher menu-element s-menu"
|
class="view-switcher menu-element s-menu-btn"
|
||||||
ng-if="view.length > 1"
|
ng-if="view.length > 1"
|
||||||
ng-controller="ClickAwayController as toggle"
|
ng-controller="ClickAwayController as toggle"
|
||||||
>
|
>
|
||||||
|
@ -19,53 +19,57 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
|
<div ng-controller="TimeRangeController">
|
||||||
<div class="l-time-controller" ng-controller="TimeRangeController">
|
|
||||||
<div class="l-time-range-inputs-holder">
|
<div class="l-time-range-inputs-holder">
|
||||||
Start: {{startOuterText}}
|
<span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
|
||||||
<span ng-controller="ToggleController as t">
|
<span class="l-time-range-input" ng-controller="ToggleController as t1">
|
||||||
<a class="ui-symbol" ng-click="t.toggle()">p</a>
|
<!--<span class="lbl">Start</span>-->
|
||||||
<mct-popup ng-if="t.isActive()">
|
<span class="s-btn time-range-start">
|
||||||
<div style="background: #222;"
|
<input type="text"
|
||||||
mct-click-elsewhere="t.setState(false)">
|
ng-model="boundsModel.start"
|
||||||
<mct-control key="'datetime-picker'"
|
ng-class="{ error: !boundsModel.startValid }">
|
||||||
ng-model="ngModel.outer"
|
</input>
|
||||||
field="'start'"
|
<a class="ui-symbol icon icon-calendar" ng-click="t1.toggle()"></a>
|
||||||
options="{ hours: true }">
|
<mct-popup ng-if="t1.isActive()">
|
||||||
</mct-control>
|
<div mct-click-elsewhere="t1.setState(false)">
|
||||||
</div>
|
<mct-control key="'datetime-picker'"
|
||||||
</mct-popup>
|
ng-model="ngModel.outer"
|
||||||
|
field="'start'"
|
||||||
|
options="{ hours: true }">
|
||||||
|
</mct-control>
|
||||||
|
</div>
|
||||||
|
</mct-popup>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
End: {{endOuterText}}
|
<span class="l-time-range-inputs-elem lbl">to</span>
|
||||||
<span ng-controller="ToggleController as t2">
|
|
||||||
<a class="ui-symbol" ng-click="t2.toggle()">p</a>
|
<span class="l-time-range-input" ng-controller="ToggleController as t2">
|
||||||
<mct-popup ng-if="t2.isActive()">
|
<!--<span class="lbl">End</span>-->
|
||||||
<div style="background: #222;"
|
<span class="s-btn l-time-range-input">
|
||||||
mct-click-elsewhere="t2.setState(false)">
|
<input type="text"
|
||||||
<mct-control key="'datetime-picker'"
|
ng-model="boundsModel.end"
|
||||||
ng-model="ngModel.outer"
|
ng-class="{ error: !boundsModel.endValid }">
|
||||||
field="'end'"
|
</input>
|
||||||
options="{ hours: true }">
|
<a class="ui-symbol icon icon-calendar" ng-click="t2.toggle()">
|
||||||
</mct-control>
|
</a>
|
||||||
</div>
|
<mct-popup ng-if="t2.isActive()">
|
||||||
</mct-popup>
|
<div mct-click-elsewhere="t2.setState(false)">
|
||||||
|
<mct-control key="'datetime-picker'"
|
||||||
|
ng-model="ngModel.outer"
|
||||||
|
field="'end'"
|
||||||
|
options="{ hours: true }">
|
||||||
|
</mct-control>
|
||||||
|
</div>
|
||||||
|
</mct-popup>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="l-time-range-slider-holder">
|
<div class="l-time-range-slider-holder">
|
||||||
<div class="l-time-range-slider">
|
<div class="l-time-range-slider">
|
||||||
<div class="slider"
|
<div class="slider"
|
||||||
mct-resize="spanWidth = bounds.width">
|
mct-resize="spanWidth = bounds.width">
|
||||||
<div class="slot range-holder">
|
|
||||||
<div class="range"
|
|
||||||
mct-drag-down="startMiddleDrag()"
|
|
||||||
mct-drag="middleDrag(delta[0])"
|
|
||||||
ng-style="{ left: startInnerPct, right: endInnerPct}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="knob knob-l"
|
<div class="knob knob-l"
|
||||||
mct-drag-down="startLeftDrag()"
|
mct-drag-down="startLeftDrag()"
|
||||||
mct-drag="leftDrag(delta[0])"
|
mct-drag="leftDrag(delta[0])"
|
||||||
@ -78,6 +82,14 @@
|
|||||||
ng-style="{ right: endInnerPct }">
|
ng-style="{ right: endInnerPct }">
|
||||||
<div class="range-value">{{endInnerText}}</div>
|
<div class="range-value">{{endInnerText}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="slot range-holder">
|
||||||
|
<div class="range"
|
||||||
|
mct-drag-down="startMiddleDrag()"
|
||||||
|
mct-drag="middleDrag(delta[0])"
|
||||||
|
ng-style="{ left: startInnerPct, right: endInnerPct}">
|
||||||
|
<div class="toi-line"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -138,7 +138,7 @@ define(
|
|||||||
$scope.ngModel[$scope.field] = m.valueOf();
|
$scope.ngModel[$scope.field] = m.valueOf();
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.isSelectable = function (cell) {
|
$scope.isInCurrentMonth = function (cell) {
|
||||||
return cell.month === month;
|
return cell.month === month;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ define(
|
|||||||
function (moment) {
|
function (moment) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
|
||||||
|
TICK_SPACING_PX = 150;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @memberof platform/commonUI/general
|
* @memberof platform/commonUI/general
|
||||||
@ -34,12 +35,23 @@ define(
|
|||||||
*/
|
*/
|
||||||
function TimeConductorController($scope, now) {
|
function TimeConductorController($scope, now) {
|
||||||
var tickCount = 2,
|
var tickCount = 2,
|
||||||
|
innerMinimumSpan = 1000, // 1 second
|
||||||
|
outerMinimumSpan = 1000 * 60 * 60, // 1 hour
|
||||||
initialDragValue;
|
initialDragValue;
|
||||||
|
|
||||||
function formatTimestamp(ts) {
|
function formatTimestamp(ts) {
|
||||||
return moment.utc(ts).format(DATE_FORMAT);
|
return moment.utc(ts).format(DATE_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseTimestamp(text) {
|
||||||
|
var m = moment.utc(text, DATE_FORMAT);
|
||||||
|
if (m.isValid()) {
|
||||||
|
return m.valueOf();
|
||||||
|
} else {
|
||||||
|
throw new Error("Could not parse " + text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// From 0.0-1.0 to "0%"-"1%"
|
// From 0.0-1.0 to "0%"-"1%"
|
||||||
function toPercent(p) {
|
function toPercent(p) {
|
||||||
return (100 * p) + "%";
|
return (100 * p) + "%";
|
||||||
@ -59,8 +71,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateSpanWidth(w) {
|
function updateSpanWidth(w) {
|
||||||
// Space about 100px apart
|
tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2);
|
||||||
tickCount = Math.max(Math.floor(w / 100), 2);
|
|
||||||
updateTicks();
|
updateTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +101,25 @@ define(
|
|||||||
return { start: bounds.start, end: bounds.end };
|
return { start: bounds.start, end: bounds.end };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateBoundsTextForProperty(ngModel, property) {
|
||||||
|
try {
|
||||||
|
if (!$scope.boundsModel[property] ||
|
||||||
|
parseTimestamp($scope.boundsModel[property]) !==
|
||||||
|
ngModel.outer[property]) {
|
||||||
|
$scope.boundsModel[property] =
|
||||||
|
formatTimestamp(ngModel.outer[property]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// User-entered text is invalid, so leave it be
|
||||||
|
// until they fix it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBoundsText(ngModel) {
|
||||||
|
updateBoundsTextForProperty(ngModel, 'start');
|
||||||
|
updateBoundsTextForProperty(ngModel, 'end');
|
||||||
|
}
|
||||||
|
|
||||||
function updateViewFromModel(ngModel) {
|
function updateViewFromModel(ngModel) {
|
||||||
var t = now();
|
var t = now();
|
||||||
|
|
||||||
@ -98,8 +128,7 @@ define(
|
|||||||
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
|
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
|
||||||
|
|
||||||
// First, dates for the date pickers for outer bounds
|
// First, dates for the date pickers for outer bounds
|
||||||
$scope.startOuterDate = new Date(ngModel.outer.start);
|
updateBoundsText(ngModel);
|
||||||
$scope.endOuterDate = new Date(ngModel.outer.end);
|
|
||||||
|
|
||||||
// Then various updates for the inner span
|
// Then various updates for the inner span
|
||||||
updateViewForInnerSpanFromModel(ngModel);
|
updateViewForInnerSpanFromModel(ngModel);
|
||||||
@ -139,7 +168,7 @@ define(
|
|||||||
$scope.ngModel.inner.start = clamp(
|
$scope.ngModel.inner.start = clamp(
|
||||||
initialDragValue + delta,
|
initialDragValue + delta,
|
||||||
$scope.ngModel.outer.start,
|
$scope.ngModel.outer.start,
|
||||||
$scope.ngModel.inner.end
|
$scope.ngModel.inner.end - innerMinimumSpan
|
||||||
);
|
);
|
||||||
updateViewFromModel($scope.ngModel);
|
updateViewFromModel($scope.ngModel);
|
||||||
}
|
}
|
||||||
@ -148,7 +177,7 @@ define(
|
|||||||
var delta = toMillis(pixels);
|
var delta = toMillis(pixels);
|
||||||
$scope.ngModel.inner.end = clamp(
|
$scope.ngModel.inner.end = clamp(
|
||||||
initialDragValue + delta,
|
initialDragValue + delta,
|
||||||
$scope.ngModel.inner.start,
|
$scope.ngModel.inner.start + innerMinimumSpan,
|
||||||
$scope.ngModel.outer.end
|
$scope.ngModel.outer.end
|
||||||
);
|
);
|
||||||
updateViewFromModel($scope.ngModel);
|
updateViewFromModel($scope.ngModel);
|
||||||
@ -174,30 +203,76 @@ define(
|
|||||||
|
|
||||||
function updateOuterStart(t) {
|
function updateOuterStart(t) {
|
||||||
var ngModel = $scope.ngModel;
|
var ngModel = $scope.ngModel;
|
||||||
ngModel.outer.end =
|
|
||||||
Math.max(ngModel.outer.start, ngModel.outer.end);
|
ngModel.outer.start = t;
|
||||||
|
|
||||||
|
ngModel.outer.end = Math.max(
|
||||||
|
ngModel.outer.start + outerMinimumSpan,
|
||||||
|
ngModel.outer.end
|
||||||
|
);
|
||||||
|
|
||||||
ngModel.inner.start =
|
ngModel.inner.start =
|
||||||
Math.max(ngModel.outer.start, ngModel.inner.start);
|
Math.max(ngModel.outer.start, ngModel.inner.start);
|
||||||
ngModel.inner.end =
|
ngModel.inner.end = Math.max(
|
||||||
Math.max(ngModel.outer.start, ngModel.inner.end);
|
ngModel.inner.start + innerMinimumSpan,
|
||||||
|
ngModel.inner.end
|
||||||
$scope.startOuterText = formatTimestamp(t);
|
);
|
||||||
|
|
||||||
updateViewForInnerSpanFromModel(ngModel);
|
updateViewForInnerSpanFromModel(ngModel);
|
||||||
|
updateTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateOuterEnd(t) {
|
function updateOuterEnd(t) {
|
||||||
var ngModel = $scope.ngModel;
|
var ngModel = $scope.ngModel;
|
||||||
ngModel.outer.start =
|
|
||||||
Math.min(ngModel.outer.end, ngModel.outer.start);
|
ngModel.outer.end = t;
|
||||||
ngModel.inner.start =
|
|
||||||
Math.min(ngModel.outer.end, ngModel.inner.start);
|
ngModel.outer.start = Math.min(
|
||||||
|
ngModel.outer.end - outerMinimumSpan,
|
||||||
|
ngModel.outer.start
|
||||||
|
);
|
||||||
|
|
||||||
ngModel.inner.end =
|
ngModel.inner.end =
|
||||||
Math.min(ngModel.outer.end, ngModel.inner.end);
|
Math.min(ngModel.outer.end, ngModel.inner.end);
|
||||||
|
ngModel.inner.start = Math.min(
|
||||||
$scope.endOuterText = formatTimestamp(t);
|
ngModel.inner.end - innerMinimumSpan,
|
||||||
|
ngModel.inner.start
|
||||||
|
);
|
||||||
|
|
||||||
updateViewForInnerSpanFromModel(ngModel);
|
updateViewForInnerSpanFromModel(ngModel);
|
||||||
|
updateTicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStartFromText(value) {
|
||||||
|
try {
|
||||||
|
updateOuterStart(parseTimestamp(value));
|
||||||
|
updateBoundsTextForProperty($scope.ngModel, 'end');
|
||||||
|
$scope.boundsModel.startValid = true;
|
||||||
|
} catch (e) {
|
||||||
|
$scope.boundsModel.startValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEndFromText(value) {
|
||||||
|
try {
|
||||||
|
updateOuterEnd(parseTimestamp(value));
|
||||||
|
updateBoundsTextForProperty($scope.ngModel, 'start');
|
||||||
|
$scope.boundsModel.endValid = true;
|
||||||
|
} catch (e) {
|
||||||
|
$scope.boundsModel.endValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStartFromPicker(value) {
|
||||||
|
updateOuterStart(value);
|
||||||
|
updateBoundsText($scope.ngModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEndFromPicker(value) {
|
||||||
|
updateOuterEnd(value);
|
||||||
|
updateBoundsText($scope.ngModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.startLeftDrag = startLeftDrag;
|
$scope.startLeftDrag = startLeftDrag;
|
||||||
@ -209,14 +284,17 @@ define(
|
|||||||
|
|
||||||
$scope.state = false;
|
$scope.state = false;
|
||||||
$scope.ticks = [];
|
$scope.ticks = [];
|
||||||
|
$scope.boundsModel = {};
|
||||||
|
|
||||||
// Initialize scope to defaults
|
// Initialize scope to defaults
|
||||||
updateViewFromModel($scope.ngModel);
|
updateViewFromModel($scope.ngModel);
|
||||||
|
|
||||||
$scope.$watchCollection("ngModel", updateViewFromModel);
|
$scope.$watchCollection("ngModel", updateViewFromModel);
|
||||||
$scope.$watch("spanWidth", updateSpanWidth);
|
$scope.$watch("spanWidth", updateSpanWidth);
|
||||||
$scope.$watch("ngModel.outer.start", updateOuterStart);
|
$scope.$watch("ngModel.outer.start", updateStartFromPicker);
|
||||||
$scope.$watch("ngModel.outer.end", updateOuterEnd);
|
$scope.$watch("ngModel.outer.end", updateEndFromPicker);
|
||||||
|
$scope.$watch("boundsModel.start", updateStartFromText);
|
||||||
|
$scope.$watch("boundsModel.end", updateEndFromText);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TimeConductorController;
|
return TimeConductorController;
|
||||||
|
@ -22,10 +22,15 @@
|
|||||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../../src/controllers/TimeRangeController"],
|
["../../src/controllers/TimeRangeController", "moment"],
|
||||||
function (TimeRangeController) {
|
function (TimeRangeController, moment) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var SEC = 1000,
|
||||||
|
MIN = 60 * SEC,
|
||||||
|
HOUR = 60 * MIN,
|
||||||
|
DAY = 24 * HOUR;
|
||||||
|
|
||||||
describe("The TimeRangeController", function () {
|
describe("The TimeRangeController", function () {
|
||||||
var mockScope,
|
var mockScope,
|
||||||
mockNow,
|
mockNow,
|
||||||
@ -61,6 +66,171 @@ define(
|
|||||||
.toHaveBeenCalledWith("ngModel", jasmine.any(Function));
|
.toHaveBeenCalledWith("ngModel", jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("when dragged", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockScope.ngModel = {
|
||||||
|
outer: {
|
||||||
|
start: DAY * 1000,
|
||||||
|
end: DAY * 1001
|
||||||
|
},
|
||||||
|
inner: {
|
||||||
|
start: DAY * 1000 + HOUR * 3,
|
||||||
|
end: DAY * 1001 - HOUR * 3
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mockScope.spanWidth = 1000;
|
||||||
|
fireWatch("spanWidth", mockScope.spanWidth);
|
||||||
|
fireWatchCollection("ngModel", mockScope.ngModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the start time for left drags", function () {
|
||||||
|
mockScope.startLeftDrag();
|
||||||
|
mockScope.leftDrag(250);
|
||||||
|
expect(mockScope.ngModel.inner.start)
|
||||||
|
.toEqual(DAY * 1000 + HOUR * 9);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the end time for right drags", function () {
|
||||||
|
mockScope.startRightDrag();
|
||||||
|
mockScope.rightDrag(-250);
|
||||||
|
expect(mockScope.ngModel.inner.end)
|
||||||
|
.toEqual(DAY * 1000 + HOUR * 15);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates both start and end for middle drags", function () {
|
||||||
|
mockScope.startMiddleDrag();
|
||||||
|
mockScope.middleDrag(-125);
|
||||||
|
expect(mockScope.ngModel.inner).toEqual({
|
||||||
|
start: DAY * 1000,
|
||||||
|
end: DAY * 1000 + HOUR * 18
|
||||||
|
});
|
||||||
|
mockScope.middleDrag(250);
|
||||||
|
expect(mockScope.ngModel.inner).toEqual({
|
||||||
|
start: DAY * 1000 + HOUR * 6,
|
||||||
|
end: DAY * 1001
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enforces a minimum inner span", function () {
|
||||||
|
mockScope.startRightDrag();
|
||||||
|
mockScope.rightDrag(-9999999);
|
||||||
|
expect(mockScope.ngModel.inner.end)
|
||||||
|
.toBeGreaterThan(mockScope.ngModel.inner.start);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when outer bounds are changed", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockScope.ngModel = {
|
||||||
|
outer: {
|
||||||
|
start: DAY * 1000,
|
||||||
|
end: DAY * 1001
|
||||||
|
},
|
||||||
|
inner: {
|
||||||
|
start: DAY * 1000 + HOUR * 3,
|
||||||
|
end: DAY * 1001 - HOUR * 3
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mockScope.spanWidth = 1000;
|
||||||
|
fireWatch("spanWidth", mockScope.spanWidth);
|
||||||
|
fireWatchCollection("ngModel", mockScope.ngModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enforces a minimum outer span", function () {
|
||||||
|
mockScope.ngModel.outer.end =
|
||||||
|
mockScope.ngModel.outer.start - DAY * 100;
|
||||||
|
fireWatch(
|
||||||
|
"ngModel.outer.end",
|
||||||
|
mockScope.ngModel.outer.end
|
||||||
|
);
|
||||||
|
expect(mockScope.ngModel.outer.end)
|
||||||
|
.toBeGreaterThan(mockScope.ngModel.outer.start);
|
||||||
|
|
||||||
|
mockScope.ngModel.outer.start =
|
||||||
|
mockScope.ngModel.outer.end + DAY * 100;
|
||||||
|
fireWatch(
|
||||||
|
"ngModel.outer.start",
|
||||||
|
mockScope.ngModel.outer.start
|
||||||
|
);
|
||||||
|
expect(mockScope.ngModel.outer.end)
|
||||||
|
.toBeGreaterThan(mockScope.ngModel.outer.start);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enforces a minimum inner span when outer span changes", function () {
|
||||||
|
mockScope.ngModel.outer.end =
|
||||||
|
mockScope.ngModel.outer.start - DAY * 100;
|
||||||
|
fireWatch(
|
||||||
|
"ngModel.outer.end",
|
||||||
|
mockScope.ngModel.outer.end
|
||||||
|
);
|
||||||
|
expect(mockScope.ngModel.inner.end)
|
||||||
|
.toBeGreaterThan(mockScope.ngModel.inner.start);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("by typing", function () {
|
||||||
|
it("updates models", function () {
|
||||||
|
var newStart = "1977-05-25 17:30:00",
|
||||||
|
newEnd = "2015-12-18 03:30:00";
|
||||||
|
|
||||||
|
mockScope.boundsModel.start = newStart;
|
||||||
|
fireWatch("boundsModel.start", newStart);
|
||||||
|
expect(mockScope.ngModel.outer.start)
|
||||||
|
.toEqual(moment.utc(newStart).valueOf());
|
||||||
|
expect(mockScope.boundsModel.startValid)
|
||||||
|
.toBeTruthy();
|
||||||
|
|
||||||
|
mockScope.boundsModel.end = newEnd;
|
||||||
|
fireWatch("boundsModel.end", newEnd);
|
||||||
|
expect(mockScope.ngModel.outer.end)
|
||||||
|
.toEqual(moment.utc(newEnd).valueOf());
|
||||||
|
expect(mockScope.boundsModel.endValid)
|
||||||
|
.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("displays error state", function () {
|
||||||
|
var newStart = "Not a date",
|
||||||
|
newEnd = "Definitely not a date",
|
||||||
|
oldStart = mockScope.ngModel.outer.start,
|
||||||
|
oldEnd = mockScope.ngModel.outer.end;
|
||||||
|
|
||||||
|
mockScope.boundsModel.start = newStart;
|
||||||
|
fireWatch("boundsModel.start", newStart);
|
||||||
|
expect(mockScope.ngModel.outer.start)
|
||||||
|
.toEqual(oldStart);
|
||||||
|
expect(mockScope.boundsModel.startValid)
|
||||||
|
.toBeFalsy();
|
||||||
|
|
||||||
|
mockScope.boundsModel.end = newEnd;
|
||||||
|
fireWatch("boundsModel.end", newEnd);
|
||||||
|
expect(mockScope.ngModel.outer.end)
|
||||||
|
.toEqual(oldEnd);
|
||||||
|
expect(mockScope.boundsModel.endValid)
|
||||||
|
.toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not modify user input", function () {
|
||||||
|
// Don't want the controller "fixing" bad or
|
||||||
|
// irregularly-formatted input out from under
|
||||||
|
// the user's fingertips.
|
||||||
|
var newStart = "Not a date",
|
||||||
|
newEnd = "2015-3-3 01:02:04",
|
||||||
|
oldStart = mockScope.ngModel.outer.start,
|
||||||
|
oldEnd = mockScope.ngModel.outer.end;
|
||||||
|
|
||||||
|
mockScope.boundsModel.start = newStart;
|
||||||
|
fireWatch("boundsModel.start", newStart);
|
||||||
|
expect(mockScope.boundsModel.start)
|
||||||
|
.toEqual(newStart);
|
||||||
|
|
||||||
|
mockScope.boundsModel.end = newEnd;
|
||||||
|
fireWatch("boundsModel.end", newEnd);
|
||||||
|
expect(mockScope.boundsModel.end)
|
||||||
|
.toEqual(newEnd);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,12 +7,14 @@ $colorKey: #0099cc;
|
|||||||
$colorKeySelectedBg: #005177;
|
$colorKeySelectedBg: #005177;
|
||||||
$colorKeyFg: #fff;
|
$colorKeyFg: #fff;
|
||||||
$colorInteriorBorder: rgba($colorBodyFg, 0.1);
|
$colorInteriorBorder: rgba($colorBodyFg, 0.1);
|
||||||
|
$colorA: #ccc;
|
||||||
|
$colorAHov: #fff;
|
||||||
$contrastRatioPercent: 7%;
|
$contrastRatioPercent: 7%;
|
||||||
$basicCr: 2px;
|
$basicCr: 2px;
|
||||||
$controlCr: 3px;
|
$controlCr: 3px;
|
||||||
$smallCr: 2px;
|
$smallCr: 2px;
|
||||||
|
|
||||||
// Buttons
|
// Buttons and Controls
|
||||||
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent); //
|
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent); //
|
||||||
$colorBtnFg: $colorBodyFg;
|
$colorBtnFg: $colorBodyFg;
|
||||||
$colorBtnMajorBg: $colorKey;
|
$colorBtnMajorBg: $colorKey;
|
||||||
@ -20,6 +22,18 @@ $colorBtnMajorFg: $colorKeyFg;
|
|||||||
$colorBtnIcon: $colorKey;
|
$colorBtnIcon: $colorKey;
|
||||||
$colorInvokeMenu: #fff;
|
$colorInvokeMenu: #fff;
|
||||||
$contrastInvokeMenuPercent: 20%;
|
$contrastInvokeMenuPercent: 20%;
|
||||||
|
$shdwBtns: rgba(black, 0.2) 0 1px 2px;
|
||||||
|
$sliderColorBase: $colorKey;
|
||||||
|
$sliderColorRangeHolder: rgba(black, 0.1);
|
||||||
|
$sliderColorRange: rgba($sliderColorBase, 0.3);
|
||||||
|
$sliderColorRangeHov: rgba($sliderColorBase, 0.5);
|
||||||
|
$sliderColorKnob: rgba($sliderColorBase, 0.6);
|
||||||
|
$sliderColorKnobHov: $sliderColorBase;
|
||||||
|
$sliderColorRangeValHovBg: rgba($sliderColorBase, 0.1);
|
||||||
|
$sliderColorRangeValHovFg: $colorKeyFg;
|
||||||
|
$sliderKnobW: nth($ueTimeControlH,2)/2;
|
||||||
|
$timeControllerToiLineColor: #00c2ff;
|
||||||
|
$timeControllerToiLineColorHov: #fff;
|
||||||
|
|
||||||
// General Colors
|
// General Colors
|
||||||
$colorAlt1: #ffc700;
|
$colorAlt1: #ffc700;
|
||||||
@ -34,6 +48,7 @@ $colorFormSectionHeader: rgba(#000, 0.2);
|
|||||||
$colorInvokeMenu: #fff;
|
$colorInvokeMenu: #fff;
|
||||||
$colorObjHdrTxt: $colorBodyFg;
|
$colorObjHdrTxt: $colorBodyFg;
|
||||||
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
||||||
|
$colorTick: rgba(white, 0.2);
|
||||||
|
|
||||||
// Menu colors
|
// Menu colors
|
||||||
$colorMenuBg: pullForward($colorBodyBg, 23%);
|
$colorMenuBg: pullForward($colorBodyBg, 23%);
|
||||||
@ -99,16 +114,17 @@ $colorItemBgSelected: $colorKey;
|
|||||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||||
$colorTabBodyBg: darken($colorBodyBg, 10%);
|
$colorTabBodyBg: darken($colorBodyBg, 10%);
|
||||||
$colorTabBodyFg: lighten($colorTabBodyBg, 40%);
|
$colorTabBodyFg: lighten($colorTabBodyBg, 40%);
|
||||||
$colorTabHeaderBg: lighten($colorBodyBg, 10%);
|
$colorTabHeaderBg: rgba(white, 0.1); // lighten($colorBodyBg, 10%);
|
||||||
$colorTabHeaderFg: lighten($colorTabHeaderBg, 40%);
|
$colorTabHeaderFg: $colorBodyFg; //lighten($colorTabHeaderBg, 40%);
|
||||||
$colorTabHeaderBorder: $colorBodyBg;
|
$colorTabHeaderBorder: $colorBodyBg;
|
||||||
|
|
||||||
// Plot
|
// Plot
|
||||||
$colorPlotBg: rgba(black, 0.1);
|
$colorPlotBg: rgba(black, 0.1);
|
||||||
$colorPlotFg: $colorBodyFg;
|
$colorPlotFg: $colorBodyFg;
|
||||||
$colorPlotHash: rgba(white, 0.2);
|
$colorPlotHash: $colorTick;
|
||||||
$stylePlotHash: dashed;
|
$stylePlotHash: dashed;
|
||||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||||
|
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
$colorItemTreeIcon: $colorKey;
|
$colorItemTreeIcon: $colorKey;
|
||||||
@ -139,5 +155,16 @@ $colorGrippyInteriorHover: $colorKey;
|
|||||||
// Mobile
|
// Mobile
|
||||||
$colorMobilePaneLeft: darken($colorBodyBg, 5%);
|
$colorMobilePaneLeft: darken($colorBodyBg, 5%);
|
||||||
|
|
||||||
|
// Datetime Picker
|
||||||
|
$colorCalCellHovBg: $colorKey;
|
||||||
|
$colorCalCellHovFg: $colorKeyFg;
|
||||||
|
$colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||||
|
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||||
|
$colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
|
||||||
|
|
||||||
// About Screen
|
// About Screen
|
||||||
$colorAboutLink: #84b3ff;
|
$colorAboutLink: #84b3ff;
|
||||||
|
|
||||||
|
// Loading
|
||||||
|
$colorLoadingBg: rgba($colorBodyFg, 0.2);
|
||||||
|
$colorLoadingFg: $colorAlt1;
|
@ -1,13 +1,13 @@
|
|||||||
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg, $hover: false) {
|
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg, $hover: false) {
|
||||||
@include containerBase($bg, $fg);
|
@include containerBase($bg, $fg);
|
||||||
@include background-image(linear-gradient(lighten($bg, 5%), $bg));
|
@include background-image(linear-gradient(lighten($bg, 5%), $bg));
|
||||||
@include boxShdwSubtle();
|
@include boxShdw($shdwBtns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin btnSubtle($bg: $colorBodyBg, $bgHov: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
|
@mixin btnSubtle($bg: $colorBodyBg, $bgHov: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
|
||||||
@include containerSubtle($bg, $fg);
|
@include containerSubtle($bg, $fg);
|
||||||
@include btnBase($bg, linear-gradient(lighten($bg, 15%), lighten($bg, 10%)), $fg, $ic);
|
@include btnBase($bg, linear-gradient(lighten($bg, 15%), lighten($bg, 10%)), $fg, $ic);
|
||||||
@include text-shadow(rgba(black, 0.3) 0 1px 1px);
|
@include text-shadow($shdwItemText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@function pullForward($c: $colorBodyBg, $p: 20%) {
|
@function pullForward($c: $colorBodyBg, $p: 20%) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,12 +7,14 @@ $colorKey: #0099cc;
|
|||||||
$colorKeySelectedBg: $colorKey;
|
$colorKeySelectedBg: $colorKey;
|
||||||
$colorKeyFg: #fff;
|
$colorKeyFg: #fff;
|
||||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||||
|
$colorA: #999;
|
||||||
|
$colorAHov: $colorKey;
|
||||||
$contrastRatioPercent: 40%;
|
$contrastRatioPercent: 40%;
|
||||||
$basicCr: 4px;
|
$basicCr: 4px;
|
||||||
$controlCr: $basicCr;
|
$controlCr: $basicCr;
|
||||||
$smallCr: 3px;
|
$smallCr: 3px;
|
||||||
|
|
||||||
// Buttons
|
// Buttons and Controls
|
||||||
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
||||||
$colorBtnFg: #fff;
|
$colorBtnFg: #fff;
|
||||||
$colorBtnMajorBg: $colorKey;
|
$colorBtnMajorBg: $colorKey;
|
||||||
@ -20,9 +22,21 @@ $colorBtnMajorFg: $colorKeyFg;
|
|||||||
$colorBtnIcon: #eee;
|
$colorBtnIcon: #eee;
|
||||||
$colorInvokeMenu: #000;
|
$colorInvokeMenu: #000;
|
||||||
$contrastInvokeMenuPercent: 40%;
|
$contrastInvokeMenuPercent: 40%;
|
||||||
|
$shdwBtns: none;
|
||||||
|
$sliderColorBase: $colorKey;
|
||||||
|
$sliderColorRangeHolder: rgba(black, 0.07);
|
||||||
|
$sliderColorRange: rgba($sliderColorBase, 0.2);
|
||||||
|
$sliderColorRangeHov: rgba($sliderColorBase, 0.4);
|
||||||
|
$sliderColorKnob: rgba($sliderColorBase, 0.5);
|
||||||
|
$sliderColorKnobHov: rgba($sliderColorBase, 0.7);
|
||||||
|
$sliderColorRangeValHovBg: $sliderColorRange; //rgba($sliderColorBase, 0.1);
|
||||||
|
$sliderColorRangeValHovFg: $colorBodyFg;
|
||||||
|
$sliderKnobW: nth($ueTimeControlH,2)/2;
|
||||||
|
$timeControllerToiLineColor: $colorBodyFg;
|
||||||
|
$timeControllerToiLineColorHov: #0052b5;
|
||||||
|
|
||||||
// General Colors
|
// General Colors
|
||||||
$colorAlt1: #ff6600;
|
$colorAlt1: #776ba2;
|
||||||
$colorAlert: #ff3c00;
|
$colorAlert: #ff3c00;
|
||||||
$colorIconLink: #49dedb;
|
$colorIconLink: #49dedb;
|
||||||
$colorPausedBg: #ff9900;
|
$colorPausedBg: #ff9900;
|
||||||
@ -32,6 +46,7 @@ $colorGridLines: rgba(#000, 0.05);
|
|||||||
$colorInvokeMenu: #fff;
|
$colorInvokeMenu: #fff;
|
||||||
$colorObjHdrTxt: $colorBodyFg;
|
$colorObjHdrTxt: $colorBodyFg;
|
||||||
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
||||||
|
$colorTick: rgba(black, 0.2);
|
||||||
|
|
||||||
// Menu colors
|
// Menu colors
|
||||||
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
||||||
@ -104,9 +119,10 @@ $colorTabHeaderBorder: $colorBodyBg;
|
|||||||
// Plot
|
// Plot
|
||||||
$colorPlotBg: rgba(black, 0.05);
|
$colorPlotBg: rgba(black, 0.05);
|
||||||
$colorPlotFg: $colorBodyFg;
|
$colorPlotFg: $colorBodyFg;
|
||||||
$colorPlotHash: rgba(black, 0.2);
|
$colorPlotHash: $colorTick;
|
||||||
$stylePlotHash: dashed;
|
$stylePlotHash: dashed;
|
||||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||||
|
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
$colorItemTreeIcon: $colorKey;
|
$colorItemTreeIcon: $colorKey;
|
||||||
@ -137,5 +153,16 @@ $colorGrippyInteriorHover: $colorBodyBg;
|
|||||||
// Mobile
|
// Mobile
|
||||||
$colorMobilePaneLeft: darken($colorBodyBg, 2%);
|
$colorMobilePaneLeft: darken($colorBodyBg, 2%);
|
||||||
|
|
||||||
|
// Datetime Picker, Calendar
|
||||||
|
$colorCalCellHovBg: $colorKey;
|
||||||
|
$colorCalCellHovFg: $colorKeyFg;
|
||||||
|
$colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||||
|
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||||
|
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
|
||||||
|
|
||||||
// About Screen
|
// About Screen
|
||||||
$colorAboutLink: #84b3ff;
|
$colorAboutLink: #84b3ff;
|
||||||
|
|
||||||
|
// Loading
|
||||||
|
$colorLoadingFg: $colorAlt1;
|
||||||
|
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
@ -1,5 +1,6 @@
|
|||||||
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
||||||
@include containerBase($bg, $fg);
|
@include containerBase($bg, $fg);
|
||||||
|
@include boxShdw($shdwBtns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin btnSubtle($bg: $colorBtnBg, $bgHov: none, $fg: $colorBtnFg, $ic: $colorBtnIcon) {
|
@mixin btnSubtle($bg: $colorBtnBg, $bgHov: none, $fg: $colorBtnFg, $ic: $colorBtnIcon) {
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
"depends": [
|
"depends": [
|
||||||
"persistenceService",
|
"persistenceService",
|
||||||
"$q",
|
"$q",
|
||||||
|
"now",
|
||||||
"PERSISTENCE_SPACE",
|
"PERSISTENCE_SPACE",
|
||||||
"ADDITIONAL_PERSISTENCE_SPACES"
|
"ADDITIONAL_PERSISTENCE_SPACES"
|
||||||
]
|
]
|
||||||
|
@ -29,7 +29,8 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var TOPIC_PREFIX = "mutation:";
|
var GENERAL_TOPIC = "mutation",
|
||||||
|
TOPIC_PREFIX = "mutation:";
|
||||||
|
|
||||||
// Utility function to overwrite a destination object
|
// Utility function to overwrite a destination object
|
||||||
// with the contents of a source object.
|
// with the contents of a source object.
|
||||||
@ -78,7 +79,11 @@ define(
|
|||||||
* @implements {Capability}
|
* @implements {Capability}
|
||||||
*/
|
*/
|
||||||
function MutationCapability(topic, now, domainObject) {
|
function MutationCapability(topic, now, domainObject) {
|
||||||
this.mutationTopic = topic(TOPIC_PREFIX + domainObject.getId());
|
this.generalMutationTopic =
|
||||||
|
topic(GENERAL_TOPIC);
|
||||||
|
this.specificMutationTopic =
|
||||||
|
topic(TOPIC_PREFIX + domainObject.getId());
|
||||||
|
|
||||||
this.now = now;
|
this.now = now;
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
}
|
}
|
||||||
@ -115,11 +120,17 @@ define(
|
|||||||
// mutator function has a temporary copy to work with.
|
// mutator function has a temporary copy to work with.
|
||||||
var domainObject = this.domainObject,
|
var domainObject = this.domainObject,
|
||||||
now = this.now,
|
now = this.now,
|
||||||
t = this.mutationTopic,
|
generalTopic = this.generalMutationTopic,
|
||||||
|
specificTopic = this.specificMutationTopic,
|
||||||
model = domainObject.getModel(),
|
model = domainObject.getModel(),
|
||||||
clone = JSON.parse(JSON.stringify(model)),
|
clone = JSON.parse(JSON.stringify(model)),
|
||||||
useTimestamp = arguments.length > 1;
|
useTimestamp = arguments.length > 1;
|
||||||
|
|
||||||
|
function notifyListeners(model) {
|
||||||
|
generalTopic.notify(domainObject);
|
||||||
|
specificTopic.notify(model);
|
||||||
|
}
|
||||||
|
|
||||||
// Function to handle copying values to the actual
|
// Function to handle copying values to the actual
|
||||||
function handleMutation(mutationResult) {
|
function handleMutation(mutationResult) {
|
||||||
// If mutation result was undefined, just use
|
// If mutation result was undefined, just use
|
||||||
@ -136,7 +147,7 @@ define(
|
|||||||
copyValues(model, result);
|
copyValues(model, result);
|
||||||
}
|
}
|
||||||
model.modified = useTimestamp ? timestamp : now();
|
model.modified = useTimestamp ? timestamp : now();
|
||||||
t.notify(model);
|
notifyListeners(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report the result of the mutation
|
// Report the result of the mutation
|
||||||
@ -158,7 +169,7 @@ define(
|
|||||||
* @memberof platform/core.MutationCapability#
|
* @memberof platform/core.MutationCapability#
|
||||||
*/
|
*/
|
||||||
MutationCapability.prototype.listen = function (listener) {
|
MutationCapability.prototype.listen = function (listener) {
|
||||||
return this.mutationTopic.listen(listener);
|
return this.specificMutationTopic.listen(listener);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,14 +39,16 @@ define(
|
|||||||
* @param {PersistenceService} persistenceService the service in which
|
* @param {PersistenceService} persistenceService the service in which
|
||||||
* domain object models are persisted.
|
* domain object models are persisted.
|
||||||
* @param $q Angular's $q service, for working with promises
|
* @param $q Angular's $q service, for working with promises
|
||||||
|
* @param {function} now a function which provides the current time
|
||||||
* @param {string} space the name of the persistence space(s)
|
* @param {string} space the name of the persistence space(s)
|
||||||
* from which models should be retrieved.
|
* from which models should be retrieved.
|
||||||
* @param {string} spaces additional persistence spaces to use
|
* @param {string} spaces additional persistence spaces to use
|
||||||
*/
|
*/
|
||||||
function PersistedModelProvider(persistenceService, $q, space, spaces) {
|
function PersistedModelProvider(persistenceService, $q, now, space, spaces) {
|
||||||
this.persistenceService = persistenceService;
|
this.persistenceService = persistenceService;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
this.spaces = [space].concat(spaces || []);
|
this.spaces = [space].concat(spaces || []);
|
||||||
|
this.now = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take the most recently modified model, for cases where
|
// Take the most recently modified model, for cases where
|
||||||
@ -61,7 +63,9 @@ define(
|
|||||||
PersistedModelProvider.prototype.getModels = function (ids) {
|
PersistedModelProvider.prototype.getModels = function (ids) {
|
||||||
var persistenceService = this.persistenceService,
|
var persistenceService = this.persistenceService,
|
||||||
$q = this.$q,
|
$q = this.$q,
|
||||||
spaces = this.spaces;
|
spaces = this.spaces,
|
||||||
|
space = this.space,
|
||||||
|
now = this.now;
|
||||||
|
|
||||||
// Load a single object model from any persistence spaces
|
// Load a single object model from any persistence spaces
|
||||||
function loadModel(id) {
|
function loadModel(id) {
|
||||||
@ -72,11 +76,24 @@ define(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that models read from persistence have some
|
||||||
|
// sensible timestamp indicating they've been persisted.
|
||||||
|
function addPersistedTimestamp(model) {
|
||||||
|
if (model && (model.persisted === undefined)) {
|
||||||
|
model.persisted = model.modified !== undefined ?
|
||||||
|
model.modified : now();
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
// Package the result as id->model
|
// Package the result as id->model
|
||||||
function packageResult(models) {
|
function packageResult(models) {
|
||||||
var result = {};
|
var result = {};
|
||||||
ids.forEach(function (id, index) {
|
ids.forEach(function (id, index) {
|
||||||
result[id] = models[index];
|
if (models[index]) {
|
||||||
|
result[id] = addPersistedTimestamp(models[index]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ define(
|
|||||||
* @memberof platform/core.Throttle#
|
* @memberof platform/core.Throttle#
|
||||||
*/
|
*/
|
||||||
return function (fn, delay, apply) {
|
return function (fn, delay, apply) {
|
||||||
var promise, // Promise for the result of throttled function
|
var promise,
|
||||||
args = [];
|
args = [];
|
||||||
|
|
||||||
function invoke() {
|
function invoke() {
|
||||||
|
@ -35,6 +35,7 @@ define(
|
|||||||
SPACE = "space0",
|
SPACE = "space0",
|
||||||
spaces = [ "space1" ],
|
spaces = [ "space1" ],
|
||||||
modTimes,
|
modTimes,
|
||||||
|
mockNow,
|
||||||
provider;
|
provider;
|
||||||
|
|
||||||
function mockPromise(value) {
|
function mockPromise(value) {
|
||||||
@ -55,19 +56,33 @@ define(
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
modTimes = {};
|
modTimes = {};
|
||||||
mockQ = { when: mockPromise, all: mockAll };
|
mockQ = { when: mockPromise, all: mockAll };
|
||||||
mockPersistenceService = {
|
mockPersistenceService = jasmine.createSpyObj(
|
||||||
readObject: function (space, id) {
|
'persistenceService',
|
||||||
|
[
|
||||||
|
'createObject',
|
||||||
|
'readObject',
|
||||||
|
'updateObject',
|
||||||
|
'deleteObject',
|
||||||
|
'listSpaces',
|
||||||
|
'listObjects'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockNow = jasmine.createSpy("now");
|
||||||
|
|
||||||
|
mockPersistenceService.readObject
|
||||||
|
.andCallFake(function (space, id) {
|
||||||
return mockPromise({
|
return mockPromise({
|
||||||
space: space,
|
space: space,
|
||||||
id: id,
|
id: id,
|
||||||
modified: (modTimes[space] || {})[id]
|
modified: (modTimes[space] || {})[id],
|
||||||
|
persisted: 0
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
provider = new PersistedModelProvider(
|
provider = new PersistedModelProvider(
|
||||||
mockPersistenceService,
|
mockPersistenceService,
|
||||||
mockQ,
|
mockQ,
|
||||||
|
mockNow,
|
||||||
SPACE,
|
SPACE,
|
||||||
spaces
|
spaces
|
||||||
);
|
);
|
||||||
@ -81,12 +96,13 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(models).toEqual({
|
expect(models).toEqual({
|
||||||
a: { space: SPACE, id: "a" },
|
a: { space: SPACE, id: "a", persisted: 0 },
|
||||||
x: { space: SPACE, id: "x" },
|
x: { space: SPACE, id: "x", persisted: 0 },
|
||||||
zz: { space: SPACE, id: "zz" }
|
zz: { space: SPACE, id: "zz", persisted: 0 }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("reads object models from multiple spaces", function () {
|
it("reads object models from multiple spaces", function () {
|
||||||
var models;
|
var models;
|
||||||
|
|
||||||
@ -99,9 +115,36 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(models).toEqual({
|
expect(models).toEqual({
|
||||||
a: { space: SPACE, id: "a" },
|
a: { space: SPACE, id: "a", persisted: 0 },
|
||||||
x: { space: 'space1', id: "x", modified: 12321 },
|
x: { space: 'space1', id: "x", modified: 12321, persisted: 0 },
|
||||||
zz: { space: SPACE, id: "zz" }
|
zz: { space: SPACE, id: "zz", persisted: 0 }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("ensures that persisted timestamps are present", function () {
|
||||||
|
var mockCallback = jasmine.createSpy("callback"),
|
||||||
|
testModels = {
|
||||||
|
a: { modified: 123, persisted: 1984, name: "A" },
|
||||||
|
b: { persisted: 1977, name: "B" },
|
||||||
|
c: { modified: 42, name: "C" },
|
||||||
|
d: { name: "D" }
|
||||||
|
};
|
||||||
|
|
||||||
|
mockPersistenceService.readObject.andCallFake(
|
||||||
|
function (space, id) {
|
||||||
|
return mockPromise(testModels[id]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
mockNow.andReturn(12321);
|
||||||
|
|
||||||
|
provider.getModels(Object.keys(testModels)).then(mockCallback);
|
||||||
|
|
||||||
|
expect(mockCallback).toHaveBeenCalledWith({
|
||||||
|
a: { modified: 123, persisted: 1984, name: "A" },
|
||||||
|
b: { persisted: 1977, name: "B" },
|
||||||
|
c: { modified: 42, persisted: 42, name: "C" },
|
||||||
|
d: { persisted: 12321, name: "D" }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,7 +23,23 @@
|
|||||||
{
|
{
|
||||||
"key": "conductorService",
|
"key": "conductorService",
|
||||||
"implementation": "ConductorService.js",
|
"implementation": "ConductorService.js",
|
||||||
"depends": [ "now" ]
|
"depends": [ "now", "TIME_CONDUCTOR_DOMAINS" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"key": "time-conductor",
|
||||||
|
"templateUrl": "templates/time-conductor.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"constants": [
|
||||||
|
{
|
||||||
|
"key": "TIME_CONDUCTOR_DOMAINS",
|
||||||
|
"value": [
|
||||||
|
{ "key": "time", "name": "Time" },
|
||||||
|
{ "key": "yesterday", "name": "Yesterday" }
|
||||||
|
],
|
||||||
|
"comment": "Placeholder; to be replaced by inspection of available domains."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<mct-include key="'time-controller'"
|
||||||
|
ng-model='ngModel.conductor'>
|
||||||
|
</mct-include>
|
||||||
|
<mct-control key="'select'"
|
||||||
|
ng-model='ngModel'
|
||||||
|
field="'domain'"
|
||||||
|
options="ngModel.options"
|
||||||
|
style="position: absolute; right: 0px; bottom: 46px;"
|
||||||
|
>
|
||||||
|
</mct-control>
|
@ -26,15 +26,9 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var CONDUCTOR_HEIGHT = "100px",
|
var TEMPLATE = [
|
||||||
TEMPLATE = [
|
"<mct-include key=\"'time-conductor'\" ng-model='ngModel' class='l-time-controller'>",
|
||||||
'<div style=',
|
"</mct-include>"
|
||||||
'"position: absolute; bottom: 0; width: 100%; ',
|
|
||||||
'overflow: hidden; ',
|
|
||||||
'height: ' + CONDUCTOR_HEIGHT + '">',
|
|
||||||
"<mct-include key=\"'time-controller'\" ng-model='conductor'>",
|
|
||||||
"</mct-include>",
|
|
||||||
'</div>'
|
|
||||||
].join(''),
|
].join(''),
|
||||||
THROTTLE_MS = 200,
|
THROTTLE_MS = 200,
|
||||||
GLOBAL_SHOWING = false;
|
GLOBAL_SHOWING = false;
|
||||||
@ -83,7 +77,8 @@ define(
|
|||||||
function bounds(start, end) {
|
function bounds(start, end) {
|
||||||
return {
|
return {
|
||||||
start: conductor.displayStart(),
|
start: conductor.displayStart(),
|
||||||
end: conductor.displayEnd()
|
end: conductor.displayEnd(),
|
||||||
|
domain: conductor.domain()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +89,30 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateConductorInner() {
|
function updateConductorInner() {
|
||||||
conductor.displayStart(conductorScope.conductor.inner.start);
|
var innerBounds = conductorScope.ngModel.conductor.inner;
|
||||||
conductor.displayEnd(conductorScope.conductor.inner.end);
|
conductor.displayStart(innerBounds.start);
|
||||||
|
conductor.displayEnd(innerBounds.end);
|
||||||
lastObservedBounds = lastObservedBounds || bounds();
|
lastObservedBounds = lastObservedBounds || bounds();
|
||||||
broadcastBounds();
|
broadcastBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateDomain(value) {
|
||||||
|
conductor.domain(value);
|
||||||
|
repScope.$broadcast('telemetry:display:bounds', bounds(
|
||||||
|
conductor.displayStart(),
|
||||||
|
conductor.displayEnd(),
|
||||||
|
conductor.domain()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// telemetry domain metadata -> option for a select control
|
||||||
|
function makeOption(domainOption) {
|
||||||
|
return {
|
||||||
|
name: domainOption.name,
|
||||||
|
value: domainOption.key
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
broadcastBounds = this.throttle(function () {
|
broadcastBounds = this.throttle(function () {
|
||||||
var newlyObservedBounds = bounds();
|
var newlyObservedBounds = bounds();
|
||||||
|
|
||||||
@ -112,12 +125,19 @@ define(
|
|||||||
}
|
}
|
||||||
}, THROTTLE_MS);
|
}, THROTTLE_MS);
|
||||||
|
|
||||||
conductorScope.conductor = { outer: bounds(), inner: bounds() };
|
conductorScope.ngModel = {};
|
||||||
|
conductorScope.ngModel.conductor =
|
||||||
|
{ outer: bounds(), inner: bounds() };
|
||||||
|
conductorScope.ngModel.options =
|
||||||
|
conductor.domainOptions().map(makeOption);
|
||||||
|
conductorScope.ngModel.domain = conductor.domain();
|
||||||
|
|
||||||
conductorScope
|
conductorScope
|
||||||
.$watch('conductor.inner.start', updateConductorInner);
|
.$watch('ngModel.conductor.inner.start', updateConductorInner);
|
||||||
conductorScope
|
conductorScope
|
||||||
.$watch('conductor.inner.end', updateConductorInner);
|
.$watch('ngModel.conductor.inner.end', updateConductorInner);
|
||||||
|
conductorScope
|
||||||
|
.$watch('ngModel.domain', updateDomain);
|
||||||
|
|
||||||
repScope.$on('telemetry:view', updateConductorInner);
|
repScope.$on('telemetry:view', updateConductorInner);
|
||||||
};
|
};
|
||||||
@ -141,8 +161,7 @@ define(
|
|||||||
this.conductorElement =
|
this.conductorElement =
|
||||||
this.$compile(TEMPLATE)(this.conductorScope());
|
this.$compile(TEMPLATE)(this.conductorScope());
|
||||||
this.element.after(this.conductorElement[0]);
|
this.element.after(this.conductorElement[0]);
|
||||||
this.element.addClass('abs');
|
this.element.addClass('l-controls-visible l-time-controller-visible');
|
||||||
this.element.css('bottom', CONDUCTOR_HEIGHT);
|
|
||||||
GLOBAL_SHOWING = true;
|
GLOBAL_SHOWING = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -39,12 +39,15 @@ define(
|
|||||||
* @param {Function} now a function which returns the current time
|
* @param {Function} now a function which returns the current time
|
||||||
* as a UNIX timestamp, in milliseconds
|
* as a UNIX timestamp, in milliseconds
|
||||||
*/
|
*/
|
||||||
function ConductorService(now) {
|
function ConductorService(now, domains) {
|
||||||
var initialEnd =
|
var initialEnd =
|
||||||
Math.ceil(now() / SIX_HOURS_IN_MS) * SIX_HOURS_IN_MS;
|
Math.ceil(now() / SIX_HOURS_IN_MS) * SIX_HOURS_IN_MS;
|
||||||
|
|
||||||
this.conductor =
|
this.conductor = new TimeConductor(
|
||||||
new TimeConductor(initialEnd - ONE_DAY_IN_MS, initialEnd);
|
initialEnd - ONE_DAY_IN_MS,
|
||||||
|
initialEnd,
|
||||||
|
domains
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,12 +44,14 @@ define(
|
|||||||
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
|
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
|
||||||
var conductor = this.conductorService.getConductor(),
|
var conductor = this.conductorService.getConductor(),
|
||||||
start = conductor.displayStart(),
|
start = conductor.displayStart(),
|
||||||
end = conductor.displayEnd();
|
end = conductor.displayEnd(),
|
||||||
|
domain = conductor.domain();
|
||||||
|
|
||||||
function amendRequest(request) {
|
function amendRequest(request) {
|
||||||
request = request || {};
|
request = request || {};
|
||||||
request.start = start;
|
request.start = start;
|
||||||
request.end = end;
|
request.end = end;
|
||||||
|
request.domain = domain;
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +40,10 @@ define(
|
|||||||
* @param {number} start the initial start time
|
* @param {number} start the initial start time
|
||||||
* @param {number} end the initial end time
|
* @param {number} end the initial end time
|
||||||
*/
|
*/
|
||||||
function TimeConductor(start, end) {
|
function TimeConductor(start, end, domains) {
|
||||||
this.range = { start: start, end: end };
|
this.range = { start: start, end: end };
|
||||||
|
this.domains = domains;
|
||||||
|
this.activeDomain = domains[0].key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,6 +70,34 @@ define(
|
|||||||
return this.range.end;
|
return this.range.end;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available domain options which can be used to bound time
|
||||||
|
* selection.
|
||||||
|
* @returns {TelemetryDomain[]} available domains
|
||||||
|
*/
|
||||||
|
TimeConductor.prototype.domainOptions = function () {
|
||||||
|
return this.domains;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or set (if called with an argument) the active domain.
|
||||||
|
* @param {string} [key] the key identifying the domain choice
|
||||||
|
* @returns {TelemetryDomain} the active telemetry domain
|
||||||
|
*/
|
||||||
|
TimeConductor.prototype.domain = function (key) {
|
||||||
|
function matchesKey(domain) {
|
||||||
|
return domain.key === key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.length > 0) {
|
||||||
|
if (!this.domains.some(matchesKey)) {
|
||||||
|
throw new Error("Unknown domain " + key);
|
||||||
|
}
|
||||||
|
this.activeDomain = key;
|
||||||
|
}
|
||||||
|
return this.activeDomain;
|
||||||
|
};
|
||||||
|
|
||||||
return TimeConductor;
|
return TimeConductor;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -21,12 +21,9 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global define,describe,it,expect,beforeEach,waitsFor,afterEach,jasmine*/
|
/*global define,describe,it,expect,beforeEach,waitsFor,afterEach,jasmine*/
|
||||||
|
|
||||||
/**
|
|
||||||
* EventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/23/2015.
|
|
||||||
*/
|
|
||||||
define(
|
define(
|
||||||
["../src/ConductorRepresenter"],
|
["../src/ConductorRepresenter", "./TestTimeConductor"],
|
||||||
function (ConductorRepresenter) {
|
function (ConductorRepresenter, TestTimeConductor) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var SCOPE_METHODS = [
|
var SCOPE_METHODS = [
|
||||||
@ -77,10 +74,7 @@ define(
|
|||||||
testViews = [ { someKey: "some value" } ];
|
testViews = [ { someKey: "some value" } ];
|
||||||
mockScope = jasmine.createSpyObj('scope', SCOPE_METHODS);
|
mockScope = jasmine.createSpyObj('scope', SCOPE_METHODS);
|
||||||
mockElement = jasmine.createSpyObj('element', ELEMENT_METHODS);
|
mockElement = jasmine.createSpyObj('element', ELEMENT_METHODS);
|
||||||
mockConductor = jasmine.createSpyObj(
|
mockConductor = new TestTimeConductor();
|
||||||
'conductor',
|
|
||||||
[ 'displayStart', 'displayEnd' ]
|
|
||||||
);
|
|
||||||
mockCompiledTemplate = jasmine.createSpy('template');
|
mockCompiledTemplate = jasmine.createSpy('template');
|
||||||
mockNewScope = jasmine.createSpyObj('newScope', SCOPE_METHODS);
|
mockNewScope = jasmine.createSpyObj('newScope', SCOPE_METHODS);
|
||||||
mockNewElement = jasmine.createSpyObj('newElement', ELEMENT_METHODS);
|
mockNewElement = jasmine.createSpyObj('newElement', ELEMENT_METHODS);
|
||||||
@ -135,11 +129,12 @@ define(
|
|||||||
it("exposes conductor state in scope", function () {
|
it("exposes conductor state in scope", function () {
|
||||||
mockConductor.displayStart.andReturn(1977);
|
mockConductor.displayStart.andReturn(1977);
|
||||||
mockConductor.displayEnd.andReturn(1984);
|
mockConductor.displayEnd.andReturn(1984);
|
||||||
|
mockConductor.domain.andReturn('d');
|
||||||
representer.represent(testViews[0], {});
|
representer.represent(testViews[0], {});
|
||||||
|
|
||||||
expect(mockNewScope.conductor).toEqual({
|
expect(mockNewScope.ngModel.conductor).toEqual({
|
||||||
inner: { start: 1977, end: 1984 },
|
inner: { start: 1977, end: 1984, domain: 'd' },
|
||||||
outer: { start: 1977, end: 1984 }
|
outer: { start: 1977, end: 1984, domain: 'd' }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,17 +146,27 @@ define(
|
|||||||
|
|
||||||
representer.represent(testViews[0], {});
|
representer.represent(testViews[0], {});
|
||||||
|
|
||||||
mockNewScope.conductor = testState;
|
mockNewScope.ngModel.conductor = testState;
|
||||||
|
|
||||||
fireWatch(mockNewScope, 'conductor.inner.start', testState.inner.start);
|
fireWatch(
|
||||||
|
mockNewScope,
|
||||||
|
'ngModel.conductor.inner.start',
|
||||||
|
testState.inner.start
|
||||||
|
);
|
||||||
expect(mockConductor.displayStart).toHaveBeenCalledWith(42);
|
expect(mockConductor.displayStart).toHaveBeenCalledWith(42);
|
||||||
|
|
||||||
fireWatch(mockNewScope, 'conductor.inner.end', testState.inner.end);
|
fireWatch(
|
||||||
|
mockNewScope,
|
||||||
|
'ngModel.conductor.inner.end',
|
||||||
|
testState.inner.end
|
||||||
|
);
|
||||||
expect(mockConductor.displayEnd).toHaveBeenCalledWith(1984);
|
expect(mockConductor.displayEnd).toHaveBeenCalledWith(1984);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when bounds are changing", function () {
|
describe("when bounds are changing", function () {
|
||||||
var mockThrottledFn = jasmine.createSpy('throttledFn'),
|
var startWatch = "ngModel.conductor.inner.start",
|
||||||
|
endWatch = "ngModel.conductor.inner.end",
|
||||||
|
mockThrottledFn = jasmine.createSpy('throttledFn'),
|
||||||
testBounds;
|
testBounds;
|
||||||
|
|
||||||
function fireThrottledFn() {
|
function fireThrottledFn() {
|
||||||
@ -172,7 +177,7 @@ define(
|
|||||||
mockThrottle.andReturn(mockThrottledFn);
|
mockThrottle.andReturn(mockThrottledFn);
|
||||||
representer.represent(testViews[0], {});
|
representer.represent(testViews[0], {});
|
||||||
testBounds = { start: 0, end: 1000 };
|
testBounds = { start: 0, end: 1000 };
|
||||||
mockNewScope.conductor.inner = testBounds;
|
mockNewScope.ngModel.conductor.inner = testBounds;
|
||||||
mockConductor.displayStart.andCallFake(function () {
|
mockConductor.displayStart.andCallFake(function () {
|
||||||
return testBounds.start;
|
return testBounds.start;
|
||||||
});
|
});
|
||||||
@ -184,14 +189,14 @@ define(
|
|||||||
it("does not broadcast while bounds are changing", function () {
|
it("does not broadcast while bounds are changing", function () {
|
||||||
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
||||||
testBounds.start = 100;
|
testBounds.start = 100;
|
||||||
fireWatch(mockNewScope, 'conductor.inner.start', testBounds.start);
|
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||||
testBounds.end = 500;
|
testBounds.end = 500;
|
||||||
fireWatch(mockNewScope, 'conductor.inner.end', testBounds.end);
|
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||||
fireThrottledFn();
|
fireThrottledFn();
|
||||||
testBounds.start = 200;
|
testBounds.start = 200;
|
||||||
fireWatch(mockNewScope, 'conductor.inner.start', testBounds.start);
|
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||||
testBounds.end = 400;
|
testBounds.end = 400;
|
||||||
fireWatch(mockNewScope, 'conductor.inner.end', testBounds.end);
|
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||||
fireThrottledFn();
|
fireThrottledFn();
|
||||||
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -199,17 +204,56 @@ define(
|
|||||||
it("does broadcast when bounds have stabilized", function () {
|
it("does broadcast when bounds have stabilized", function () {
|
||||||
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
||||||
testBounds.start = 100;
|
testBounds.start = 100;
|
||||||
fireWatch(mockNewScope, 'conductor.inner.start', testBounds.start);
|
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||||
testBounds.end = 500;
|
testBounds.end = 500;
|
||||||
fireWatch(mockNewScope, 'conductor.inner.end', testBounds.end);
|
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||||
fireThrottledFn();
|
fireThrottledFn();
|
||||||
fireWatch(mockNewScope, 'conductor.inner.start', testBounds.start);
|
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||||
fireWatch(mockNewScope, 'conductor.inner.end', testBounds.end);
|
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||||
fireThrottledFn();
|
fireThrottledFn();
|
||||||
expect(mockScope.$broadcast).toHaveBeenCalled();
|
expect(mockScope.$broadcast).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("exposes domain selection in scope", function () {
|
||||||
|
representer.represent(testViews[0], null);
|
||||||
|
|
||||||
|
expect(mockNewScope.ngModel.domain)
|
||||||
|
.toEqual(mockConductor.domain());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exposes domain options in scope", function () {
|
||||||
|
representer.represent(testViews[0], null);
|
||||||
|
|
||||||
|
mockConductor.domainOptions().forEach(function (option, i) {
|
||||||
|
expect(mockNewScope.ngModel.options[i].value)
|
||||||
|
.toEqual(option.key);
|
||||||
|
expect(mockNewScope.ngModel.options[i].name)
|
||||||
|
.toEqual(option.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates domain selection from scope", function () {
|
||||||
|
var choice;
|
||||||
|
representer.represent(testViews[0], null);
|
||||||
|
|
||||||
|
// Choose a domain that isn't currently selected
|
||||||
|
mockNewScope.ngModel.options.forEach(function (option) {
|
||||||
|
if (option.value !== mockNewScope.ngModel.domain) {
|
||||||
|
choice = option.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockConductor.domain)
|
||||||
|
.not.toHaveBeenCalledWith(choice);
|
||||||
|
|
||||||
|
mockNewScope.ngModel.domain = choice;
|
||||||
|
fireWatch(mockNewScope, "ngModel.domain", choice);
|
||||||
|
|
||||||
|
expect(mockConductor.domain)
|
||||||
|
.toHaveBeenCalledWith(choice);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -21,9 +21,6 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
/**
|
|
||||||
* EventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/23/2015.
|
|
||||||
*/
|
|
||||||
define(
|
define(
|
||||||
["../src/ConductorService"],
|
["../src/ConductorService"],
|
||||||
function (ConductorService) {
|
function (ConductorService) {
|
||||||
@ -38,7 +35,10 @@ define(
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockNow = jasmine.createSpy('now');
|
mockNow = jasmine.createSpy('now');
|
||||||
mockNow.andReturn(TEST_NOW);
|
mockNow.andReturn(TEST_NOW);
|
||||||
conductorService = new ConductorService(mockNow);
|
conductorService = new ConductorService(mockNow, [
|
||||||
|
{ key: "d1", name: "Domain #1" },
|
||||||
|
{ key: "d2", name: "Domain #2" }
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("initializes a time conductor around the current time", function () {
|
it("initializes a time conductor around the current time", function () {
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../src/ConductorTelemetryDecorator"],
|
["../src/ConductorTelemetryDecorator", "./TestTimeConductor"],
|
||||||
function (ConductorTelemetryDecorator) {
|
function (ConductorTelemetryDecorator, TestTimeConductor) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
describe("ConductorTelemetryDecorator", function () {
|
describe("ConductorTelemetryDecorator", function () {
|
||||||
@ -54,10 +54,7 @@ define(
|
|||||||
'conductorService',
|
'conductorService',
|
||||||
['getConductor']
|
['getConductor']
|
||||||
);
|
);
|
||||||
mockConductor = jasmine.createSpyObj(
|
mockConductor = new TestTimeConductor();
|
||||||
'conductor',
|
|
||||||
[ 'queryStart', 'queryEnd', 'displayStart', 'displayEnd' ]
|
|
||||||
);
|
|
||||||
mockPromise = jasmine.createSpyObj(
|
mockPromise = jasmine.createSpyObj(
|
||||||
'promise',
|
'promise',
|
||||||
['then']
|
['then']
|
||||||
@ -78,10 +75,9 @@ define(
|
|||||||
return j * j * j;
|
return j * j * j;
|
||||||
});
|
});
|
||||||
|
|
||||||
mockConductor.queryStart.andReturn(-12321);
|
|
||||||
mockConductor.queryEnd.andReturn(-12321);
|
|
||||||
mockConductor.displayStart.andReturn(42);
|
mockConductor.displayStart.andReturn(42);
|
||||||
mockConductor.displayEnd.andReturn(1977);
|
mockConductor.displayEnd.andReturn(1977);
|
||||||
|
mockConductor.domain.andReturn("testDomain");
|
||||||
|
|
||||||
decorator = new ConductorTelemetryDecorator(
|
decorator = new ConductorTelemetryDecorator(
|
||||||
mockConductorService,
|
mockConductorService,
|
||||||
@ -89,24 +85,72 @@ define(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("adds display start/end times to historical requests", function () {
|
|
||||||
|
describe("decorates historical requests", function () {
|
||||||
|
var request;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
decorator.requestTelemetry([{ someKey: "some value" }]);
|
||||||
|
request = mockTelemetryService.requestTelemetry
|
||||||
|
.mostRecentCall.args[0][0];
|
||||||
|
});
|
||||||
|
|
||||||
|
it("with start times", function () {
|
||||||
|
expect(request.start).toEqual(mockConductor.displayStart());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("with end times", function () {
|
||||||
|
expect(request.end).toEqual(mockConductor.displayEnd());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("with domain selection", function () {
|
||||||
|
expect(request.domain).toEqual(mockConductor.domain());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("decorates subscription requests", function () {
|
||||||
|
var request;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
var mockCallback = jasmine.createSpy('callback');
|
||||||
|
decorator.subscribe(mockCallback, [{ someKey: "some value" }]);
|
||||||
|
request = mockTelemetryService.subscribe
|
||||||
|
.mostRecentCall.args[1][0];
|
||||||
|
});
|
||||||
|
|
||||||
|
it("with start times", function () {
|
||||||
|
expect(request.start).toEqual(mockConductor.displayStart());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("with end times", function () {
|
||||||
|
expect(request.end).toEqual(mockConductor.displayEnd());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("with domain selection", function () {
|
||||||
|
expect(request.domain).toEqual(mockConductor.domain());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds display start/end times & domain selection to historical requests", function () {
|
||||||
decorator.requestTelemetry([{ someKey: "some value" }]);
|
decorator.requestTelemetry([{ someKey: "some value" }]);
|
||||||
expect(mockTelemetryService.requestTelemetry)
|
expect(mockTelemetryService.requestTelemetry)
|
||||||
.toHaveBeenCalledWith([{
|
.toHaveBeenCalledWith([{
|
||||||
someKey: "some value",
|
someKey: "some value",
|
||||||
start: mockConductor.displayStart(),
|
start: mockConductor.displayStart(),
|
||||||
end: mockConductor.displayEnd()
|
end: mockConductor.displayEnd(),
|
||||||
|
domain: jasmine.any(String)
|
||||||
}]);
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("adds display start/end times to subscription requests", function () {
|
it("adds display start/end times & domain selection to subscription requests", function () {
|
||||||
var mockCallback = jasmine.createSpy('callback');
|
var mockCallback = jasmine.createSpy('callback');
|
||||||
decorator.subscribe(mockCallback, [{ someKey: "some value" }]);
|
decorator.subscribe(mockCallback, [{ someKey: "some value" }]);
|
||||||
expect(mockTelemetryService.subscribe)
|
expect(mockTelemetryService.subscribe)
|
||||||
.toHaveBeenCalledWith(jasmine.any(Function), [{
|
.toHaveBeenCalledWith(jasmine.any(Function), [{
|
||||||
someKey: "some value",
|
someKey: "some value",
|
||||||
start: mockConductor.displayStart(),
|
start: mockConductor.displayStart(),
|
||||||
end: mockConductor.displayEnd()
|
end: mockConductor.displayEnd(),
|
||||||
|
domain: jasmine.any(String)
|
||||||
}]);
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
50
platform/features/conductor/test/TestTimeConductor.js
Normal file
50
platform/features/conductor/test/TestTimeConductor.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,spyOn*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../src/TimeConductor"],
|
||||||
|
function (TimeConductor) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function TestTimeConductor() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
TimeConductor.apply(this, [
|
||||||
|
402514200000,
|
||||||
|
444546000000,
|
||||||
|
[
|
||||||
|
{ key: "domain0", name: "Domain #1" },
|
||||||
|
{ key: "domain1", name: "Domain #2" }
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
Object.keys(TimeConductor.prototype).forEach(function (method) {
|
||||||
|
spyOn(self, method).andCallThrough();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TestTimeConductor.prototype = TimeConductor.prototype;
|
||||||
|
|
||||||
|
return TestTimeConductor;
|
||||||
|
}
|
||||||
|
);
|
@ -21,9 +21,6 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
/**
|
|
||||||
* EventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/23/2015.
|
|
||||||
*/
|
|
||||||
define(
|
define(
|
||||||
["../src/TimeConductor"],
|
["../src/TimeConductor"],
|
||||||
function (TimeConductor) {
|
function (TimeConductor) {
|
||||||
@ -32,12 +29,17 @@ define(
|
|||||||
describe("TimeConductor", function () {
|
describe("TimeConductor", function () {
|
||||||
var testStart,
|
var testStart,
|
||||||
testEnd,
|
testEnd,
|
||||||
|
testDomains,
|
||||||
conductor;
|
conductor;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testStart = 42;
|
testStart = 42;
|
||||||
testEnd = 12321;
|
testEnd = 12321;
|
||||||
conductor = new TimeConductor(testStart, testEnd);
|
testDomains = [
|
||||||
|
{ key: "d1", name: "Domain #1" },
|
||||||
|
{ key: "d2", name: "Domain #2" }
|
||||||
|
];
|
||||||
|
conductor = new TimeConductor(testStart, testEnd, testDomains);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("provides accessors for query/display start/end times", function () {
|
it("provides accessors for query/display start/end times", function () {
|
||||||
@ -52,6 +54,25 @@ define(
|
|||||||
expect(conductor.displayEnd()).toEqual(4);
|
expect(conductor.displayEnd()).toEqual(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("exposes domain options", function () {
|
||||||
|
expect(conductor.domainOptions()).toEqual(testDomains);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exposes the current domain choice", function () {
|
||||||
|
expect(conductor.domain()).toEqual(testDomains[0].key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows the domain choice to be changed", function () {
|
||||||
|
conductor.domain(testDomains[1].key);
|
||||||
|
expect(conductor.domain()).toEqual(testDomains[1].key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error on attempts to set an invalid domain", function () {
|
||||||
|
expect(function () {
|
||||||
|
conductor.domain("invalid-domain");
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div style="width: 100%; height: 100%;"
|
<div class="l-layout"
|
||||||
ng-controller="LayoutController as controller">
|
ng-controller="LayoutController as controller">
|
||||||
|
|
||||||
<div class='frame child-frame panel abs'
|
<div class='frame child-frame panel abs'
|
||||||
|
@ -105,11 +105,13 @@ define(
|
|||||||
index
|
index
|
||||||
);
|
);
|
||||||
|
|
||||||
setDisplayedValue(
|
if (index >= 0) {
|
||||||
telemetryObject,
|
setDisplayedValue(
|
||||||
telemetrySeries.getRangeValue(index),
|
telemetryObject,
|
||||||
limit && datum && limit.evaluate(datum)
|
telemetrySeries.getRangeValue(index),
|
||||||
);
|
limit && datum && limit.evaluate(datum)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the displayed value for this object
|
// Update the displayed value for this object
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="abs l-iframe">
|
<div class="l-iframe abs">
|
||||||
<iframe ng-controller="EmbeddedPageController as ctl"
|
<iframe ng-controller="EmbeddedPageController as ctl"
|
||||||
ng-src="{{ctl.trust(model.url)}}">
|
ng-src="{{ctl.trust(model.url)}}">
|
||||||
</iframe>
|
</iframe>
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
<span class="ui-symbol icon">I</span>
|
<span class="ui-symbol icon">I</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="menu-element s-menu menus-to-left"
|
<div class="menu-element s-menu-btn menus-to-left"
|
||||||
ng-if="plot.getModeOptions().length > 1"
|
ng-if="plot.getModeOptions().length > 1"
|
||||||
ng-controller="ClickAwayController as toggle">
|
ng-controller="ClickAwayController as toggle">
|
||||||
|
|
||||||
|
@ -64,6 +64,16 @@ define(
|
|||||||
this.updateTicks();
|
this.updateTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether this subplot has domain data to show for the current pan/zoom level. Absence of domain data
|
||||||
|
* implies that there is no range data displayed either
|
||||||
|
* @returns {boolean} true if domain data exists for the current pan/zoom level
|
||||||
|
*/
|
||||||
|
SubPlot.prototype.hasDomainData = function() {
|
||||||
|
return this.panZoomStack
|
||||||
|
&& this.panZoomStack.getDimensions()[0] > 0;
|
||||||
|
};
|
||||||
|
|
||||||
// Utility function for filtering out empty strings.
|
// Utility function for filtering out empty strings.
|
||||||
function isNonEmpty(v) {
|
function isNonEmpty(v) {
|
||||||
return typeof v === 'string' && v !== "";
|
return typeof v === 'string' && v !== "";
|
||||||
@ -253,7 +263,10 @@ define(
|
|||||||
this.hovering = true;
|
this.hovering = true;
|
||||||
this.subPlotBounds = $event.target.getBoundingClientRect();
|
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||||
this.mousePosition = this.toMousePosition($event);
|
this.mousePosition = this.toMousePosition($event);
|
||||||
this.updateHoverCoordinates();
|
//If there is a domain to display, show hover coordinates, otherwise hover coordinates are meaningless
|
||||||
|
if (this.hasDomainData()) {
|
||||||
|
this.updateHoverCoordinates();
|
||||||
|
}
|
||||||
if (this.marqueeStart) {
|
if (this.marqueeStart) {
|
||||||
this.updateMarqueeBox();
|
this.updateMarqueeBox();
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,8 @@ define(
|
|||||||
|
|
||||||
for (i = 0; i < count; i += 1) {
|
for (i = 0; i < count; i += 1) {
|
||||||
result.push({
|
result.push({
|
||||||
label: format(i * step + start)
|
//If data to show, display label for each tick line, otherwise show lines but suppress labels.
|
||||||
|
label: span > 0 ? format(i * step + start) : ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +157,15 @@ define(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it ("indicates when there is domain data shown", function () {
|
||||||
|
expect(subplot.hasDomainData()).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("indicates when there is no domain data shown", function () {
|
||||||
|
mockPanZoomStack.getDimensions.andReturn([0,0]);
|
||||||
|
expect(subplot.hasDomainData()).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
it("disallows marquee zoom when start and end Marquee is at the same position", function () {
|
it("disallows marquee zoom when start and end Marquee is at the same position", function () {
|
||||||
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
|
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div
|
<div
|
||||||
class="t-btn l-btn s-btn s-icon-btn s-menu menu-element t-color-palette"
|
class="t-btn l-btn s-btn s-icon-btn s-menu-btn menu-element t-color-palette"
|
||||||
ng-controller="ClickAwayController as toggle"
|
ng-controller="ClickAwayController as toggle"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="s-menu menu-element"
|
<div class="s-menu-btn menu-element"
|
||||||
ng-controller="ClickAwayController as toggle">
|
ng-controller="ClickAwayController as toggle">
|
||||||
|
|
||||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||||
|
@ -45,7 +45,15 @@
|
|||||||
"provides": "searchService",
|
"provides": "searchService",
|
||||||
"type": "provider",
|
"type": "provider",
|
||||||
"implementation": "services/GenericSearchProvider.js",
|
"implementation": "services/GenericSearchProvider.js",
|
||||||
"depends": [ "$q", "$timeout", "objectService", "workerService", "GENERIC_SEARCH_ROOTS" ]
|
"depends": [
|
||||||
|
"$q",
|
||||||
|
"$log",
|
||||||
|
"throttle",
|
||||||
|
"objectService",
|
||||||
|
"workerService",
|
||||||
|
"topic",
|
||||||
|
"GENERIC_SEARCH_ROOTS"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"provides": "searchService",
|
"provides": "searchService",
|
||||||
|
@ -31,6 +31,8 @@ define(
|
|||||||
|
|
||||||
var DEFAULT_MAX_RESULTS = 100,
|
var DEFAULT_MAX_RESULTS = 100,
|
||||||
DEFAULT_TIMEOUT = 1000,
|
DEFAULT_TIMEOUT = 1000,
|
||||||
|
MAX_CONCURRENT_REQUESTS = 100,
|
||||||
|
FLUSH_INTERVAL = 0,
|
||||||
stopTime;
|
stopTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +41,8 @@ define(
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param $q Angular's $q, for promise consolidation.
|
* @param $q Angular's $q, for promise consolidation.
|
||||||
* @param $timeout Angular's $timeout, for delayed function execution.
|
* @param $log Anglar's $log, for logging.
|
||||||
|
* @param {Function} throttle a function to throttle function invocations
|
||||||
* @param {ObjectService} objectService The service from which
|
* @param {ObjectService} objectService The service from which
|
||||||
* domain objects can be gotten.
|
* domain objects can be gotten.
|
||||||
* @param {WorkerService} workerService The service which allows
|
* @param {WorkerService} workerService The service which allows
|
||||||
@ -47,10 +50,16 @@ define(
|
|||||||
* @param {GENERIC_SEARCH_ROOTS} ROOTS An array of the root
|
* @param {GENERIC_SEARCH_ROOTS} ROOTS An array of the root
|
||||||
* domain objects' IDs.
|
* domain objects' IDs.
|
||||||
*/
|
*/
|
||||||
function GenericSearchProvider($q, $timeout, objectService, workerService, ROOTS) {
|
function GenericSearchProvider($q, $log, throttle, objectService, workerService, topic, ROOTS) {
|
||||||
var indexed = {},
|
var indexed = {},
|
||||||
|
pendingIndex = {},
|
||||||
pendingQueries = {},
|
pendingQueries = {},
|
||||||
worker = workerService.run('genericSearchWorker');
|
toRequest = [],
|
||||||
|
worker = workerService.run('genericSearchWorker'),
|
||||||
|
mutationTopic = topic("mutation"),
|
||||||
|
indexingStarted = Date.now(),
|
||||||
|
pendingRequests = 0,
|
||||||
|
scheduleFlush;
|
||||||
|
|
||||||
this.worker = worker;
|
this.worker = worker;
|
||||||
this.pendingQueries = pendingQueries;
|
this.pendingQueries = pendingQueries;
|
||||||
@ -58,23 +67,31 @@ define(
|
|||||||
// pendingQueries is a dictionary with the key value pairs st
|
// pendingQueries is a dictionary with the key value pairs st
|
||||||
// the key is the timestamp and the value is the promise
|
// the key is the timestamp and the value is the promise
|
||||||
|
|
||||||
// Tell the web worker to add a domain object's model to its list of items.
|
function scheduleIdsForIndexing(ids) {
|
||||||
function indexItem(domainObject) {
|
ids.forEach(function (id) {
|
||||||
var message;
|
if (!indexed[id] && !pendingIndex[id]) {
|
||||||
|
indexed[id] = true;
|
||||||
// undefined check
|
pendingIndex[id] = true;
|
||||||
if (domainObject && domainObject.getModel) {
|
toRequest.push(id);
|
||||||
// Using model instead of whole domain object because
|
}
|
||||||
// it's a JSON object.
|
});
|
||||||
message = {
|
scheduleFlush();
|
||||||
request: 'index',
|
|
||||||
model: domainObject.getModel(),
|
|
||||||
id: domainObject.getId()
|
|
||||||
};
|
|
||||||
worker.postMessage(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the web worker to add a domain object's model to its list of items.
|
||||||
|
function indexItem(domainObject) {
|
||||||
|
var model = domainObject.getModel();
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
request: 'index',
|
||||||
|
model: model,
|
||||||
|
id: domainObject.getId()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Array.isArray(model.composition)) {
|
||||||
|
scheduleIdsForIndexing(model.composition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handles responses from the web worker. Namely, the results of
|
// Handles responses from the web worker. Namely, the results of
|
||||||
// a search request.
|
// a search request.
|
||||||
@ -111,82 +128,48 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for getItems(). Indexes the tree.
|
function requestAndIndex(id) {
|
||||||
function indexItems(nodes) {
|
pendingRequests += 1;
|
||||||
nodes.forEach(function (node) {
|
objectService.getObjects([id]).then(function (objects) {
|
||||||
var id = node && node.getId && node.getId();
|
delete pendingIndex[id];
|
||||||
|
if (objects[id]) {
|
||||||
// If we have already indexed this item, stop here
|
indexItem(objects[id]);
|
||||||
if (indexed[id]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index each item with the web worker
|
|
||||||
indexItem(node);
|
|
||||||
indexed[id] = true;
|
|
||||||
|
|
||||||
|
|
||||||
// If this node has children, index those
|
|
||||||
if (node && node.hasCapability && node.hasCapability('composition')) {
|
|
||||||
// Make sure that this is async, so doesn't block up page
|
|
||||||
$timeout(function () {
|
|
||||||
// Get the children...
|
|
||||||
node.useCapability('composition').then(function (children) {
|
|
||||||
$timeout(function () {
|
|
||||||
// ... then index the children
|
|
||||||
if (children.constructor === Array) {
|
|
||||||
indexItems(children);
|
|
||||||
} else {
|
|
||||||
indexItems([children]);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch for changes to this item, in case it gets new children
|
|
||||||
if (node && node.hasCapability && node.hasCapability('mutation')) {
|
|
||||||
node.getCapability('mutation').listen(function (listener) {
|
|
||||||
if (listener && listener.composition) {
|
|
||||||
// If the node was mutated to have children, get the child domain objects
|
|
||||||
objectService.getObjects(listener.composition).then(function (objectsById) {
|
|
||||||
var objects = [],
|
|
||||||
id;
|
|
||||||
|
|
||||||
// Get each of the domain objects in objectsById
|
|
||||||
for (id in objectsById) {
|
|
||||||
objects.push(objectsById[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
indexItems(objects);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}, function () {
|
||||||
|
$log.warn("Failed to index domain object " + id);
|
||||||
|
}).then(function () {
|
||||||
|
pendingRequests -= 1;
|
||||||
|
scheduleFlush();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the filetree into a list
|
scheduleFlush = throttle(function flush() {
|
||||||
function getItems() {
|
var batchSize =
|
||||||
// Aquire root objects
|
Math.max(MAX_CONCURRENT_REQUESTS - pendingRequests, 0);
|
||||||
objectService.getObjects(ROOTS).then(function (objectsById) {
|
|
||||||
var objects = [],
|
|
||||||
id;
|
|
||||||
|
|
||||||
// Get each of the domain objects in objectsById
|
if (toRequest.length + pendingRequests < 1) {
|
||||||
for (id in objectsById) {
|
$log.info([
|
||||||
objects.push(objectsById[id]);
|
'GenericSearch finished indexing after ',
|
||||||
}
|
((Date.now() - indexingStarted) / 1000).toFixed(2),
|
||||||
|
' seconds.'
|
||||||
// Index all of the roots' descendents
|
].join(''));
|
||||||
indexItems(objects);
|
} else {
|
||||||
});
|
toRequest.splice(-batchSize, batchSize)
|
||||||
}
|
.forEach(requestAndIndex);
|
||||||
|
}
|
||||||
|
}, FLUSH_INTERVAL);
|
||||||
|
|
||||||
worker.onmessage = handleResponse;
|
worker.onmessage = handleResponse;
|
||||||
|
|
||||||
// Index the tree's contents once at the beginning
|
// Index the tree's contents once at the beginning
|
||||||
getItems();
|
scheduleIdsForIndexing(ROOTS);
|
||||||
|
|
||||||
|
// Re-index items when they are mutated
|
||||||
|
mutationTopic.listen(function (domainObject) {
|
||||||
|
var id = domainObject.getId();
|
||||||
|
indexed[id] = false;
|
||||||
|
scheduleIdsForIndexing([id]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,26 +31,56 @@ define(
|
|||||||
|
|
||||||
describe("The generic search provider ", function () {
|
describe("The generic search provider ", function () {
|
||||||
var mockQ,
|
var mockQ,
|
||||||
mockTimeout,
|
mockLog,
|
||||||
|
mockThrottle,
|
||||||
mockDeferred,
|
mockDeferred,
|
||||||
mockObjectService,
|
mockObjectService,
|
||||||
mockObjectPromise,
|
mockObjectPromise,
|
||||||
|
mockChainedPromise,
|
||||||
mockDomainObjects,
|
mockDomainObjects,
|
||||||
mockCapability,
|
mockCapability,
|
||||||
mockCapabilityPromise,
|
mockCapabilityPromise,
|
||||||
mockWorkerService,
|
mockWorkerService,
|
||||||
mockWorker,
|
mockWorker,
|
||||||
|
mockTopic,
|
||||||
|
mockMutationTopic,
|
||||||
mockRoots = ['root1', 'root2'],
|
mockRoots = ['root1', 'root2'],
|
||||||
|
mockThrottledFn,
|
||||||
|
throttledCallCount,
|
||||||
provider,
|
provider,
|
||||||
mockProviderResults;
|
mockProviderResults;
|
||||||
|
|
||||||
beforeEach(function () {
|
function resolveObjectPromises() {
|
||||||
var i;
|
var i;
|
||||||
|
for (i = 0; i < mockObjectPromise.then.calls.length; i += 1) {
|
||||||
|
mockChainedPromise.then.calls[i].args[0](
|
||||||
|
mockObjectPromise.then.calls[i]
|
||||||
|
.args[0](mockDomainObjects)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveThrottledFn() {
|
||||||
|
if (mockThrottledFn.calls.length > throttledCallCount) {
|
||||||
|
mockThrottle.mostRecentCall.args[0]();
|
||||||
|
throttledCallCount = mockThrottledFn.calls.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAsyncTasks() {
|
||||||
|
resolveThrottledFn();
|
||||||
|
resolveObjectPromises();
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
mockQ = jasmine.createSpyObj(
|
mockQ = jasmine.createSpyObj(
|
||||||
"$q",
|
"$q",
|
||||||
[ "defer" ]
|
[ "defer" ]
|
||||||
);
|
);
|
||||||
|
mockLog = jasmine.createSpyObj(
|
||||||
|
"$log",
|
||||||
|
[ "error", "warn", "info", "debug" ]
|
||||||
|
);
|
||||||
mockDeferred = jasmine.createSpyObj(
|
mockDeferred = jasmine.createSpyObj(
|
||||||
"deferred",
|
"deferred",
|
||||||
[ "resolve", "reject"]
|
[ "resolve", "reject"]
|
||||||
@ -58,7 +88,9 @@ define(
|
|||||||
mockDeferred.promise = "mock promise";
|
mockDeferred.promise = "mock promise";
|
||||||
mockQ.defer.andReturn(mockDeferred);
|
mockQ.defer.andReturn(mockDeferred);
|
||||||
|
|
||||||
mockTimeout = jasmine.createSpy("$timeout");
|
mockThrottle = jasmine.createSpy("throttle");
|
||||||
|
mockThrottledFn = jasmine.createSpy("throttledFn");
|
||||||
|
throttledCallCount = 0;
|
||||||
|
|
||||||
mockObjectService = jasmine.createSpyObj(
|
mockObjectService = jasmine.createSpyObj(
|
||||||
"objectService",
|
"objectService",
|
||||||
@ -68,8 +100,13 @@ define(
|
|||||||
"promise",
|
"promise",
|
||||||
[ "then", "catch" ]
|
[ "then", "catch" ]
|
||||||
);
|
);
|
||||||
|
mockChainedPromise = jasmine.createSpyObj(
|
||||||
|
"chainedPromise",
|
||||||
|
[ "then" ]
|
||||||
|
);
|
||||||
mockObjectService.getObjects.andReturn(mockObjectPromise);
|
mockObjectService.getObjects.andReturn(mockObjectPromise);
|
||||||
|
|
||||||
|
mockTopic = jasmine.createSpy('topic');
|
||||||
|
|
||||||
mockWorkerService = jasmine.createSpyObj(
|
mockWorkerService = jasmine.createSpyObj(
|
||||||
"workerService",
|
"workerService",
|
||||||
@ -87,59 +124,100 @@ define(
|
|||||||
);
|
);
|
||||||
|
|
||||||
mockDomainObjects = {};
|
mockDomainObjects = {};
|
||||||
for (i = 0; i < 4; i += 1) {
|
['a', 'root1', 'root2'].forEach(function (id) {
|
||||||
mockDomainObjects[i] = (
|
mockDomainObjects[id] = (
|
||||||
jasmine.createSpyObj(
|
jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
[ "getId", "getModel", "hasCapability", "getCapability", "useCapability" ]
|
[
|
||||||
|
"getId",
|
||||||
|
"getModel",
|
||||||
|
"hasCapability",
|
||||||
|
"getCapability",
|
||||||
|
"useCapability"
|
||||||
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
mockDomainObjects[i].getId.andReturn(i);
|
mockDomainObjects[id].getId.andReturn(id);
|
||||||
mockDomainObjects[i].getCapability.andReturn(mockCapability);
|
mockDomainObjects[id].getCapability.andReturn(mockCapability);
|
||||||
mockDomainObjects[i].useCapability.andReturn(mockCapabilityPromise);
|
mockDomainObjects[id].useCapability.andReturn(mockCapabilityPromise);
|
||||||
}
|
mockDomainObjects[id].getModel.andReturn({});
|
||||||
// Give the first object children
|
});
|
||||||
mockDomainObjects[0].hasCapability.andReturn(true);
|
|
||||||
mockCapability = jasmine.createSpyObj(
|
mockCapability = jasmine.createSpyObj(
|
||||||
"capability",
|
"capability",
|
||||||
[ "invoke", "listen" ]
|
[ "invoke", "listen" ]
|
||||||
);
|
);
|
||||||
mockCapability.invoke.andReturn(mockCapabilityPromise);
|
mockCapability.invoke.andReturn(mockCapabilityPromise);
|
||||||
mockDomainObjects[0].getCapability.andReturn(mockCapability);
|
mockDomainObjects.a.getCapability.andReturn(mockCapability);
|
||||||
|
mockMutationTopic = jasmine.createSpyObj(
|
||||||
|
'mutationTopic',
|
||||||
|
[ 'listen' ]
|
||||||
|
);
|
||||||
|
mockTopic.andCallFake(function (key) {
|
||||||
|
return key === 'mutation' && mockMutationTopic;
|
||||||
|
});
|
||||||
|
mockThrottle.andReturn(mockThrottledFn);
|
||||||
|
mockObjectPromise.then.andReturn(mockChainedPromise);
|
||||||
|
|
||||||
provider = new GenericSearchProvider(mockQ, mockTimeout, mockObjectService, mockWorkerService, mockRoots);
|
provider = new GenericSearchProvider(
|
||||||
|
mockQ,
|
||||||
|
mockLog,
|
||||||
|
mockThrottle,
|
||||||
|
mockObjectService,
|
||||||
|
mockWorkerService,
|
||||||
|
mockTopic,
|
||||||
|
mockRoots
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("indexes tree on initialization", function () {
|
it("indexes tree on initialization", function () {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
resolveThrottledFn();
|
||||||
|
|
||||||
expect(mockObjectService.getObjects).toHaveBeenCalled();
|
expect(mockObjectService.getObjects).toHaveBeenCalled();
|
||||||
expect(mockObjectPromise.then).toHaveBeenCalled();
|
expect(mockObjectPromise.then).toHaveBeenCalled();
|
||||||
|
|
||||||
// Call through the root-getting part
|
// Call through the root-getting part
|
||||||
mockObjectPromise.then.mostRecentCall.args[0](mockDomainObjects);
|
resolveObjectPromises();
|
||||||
|
|
||||||
// Call through the children-getting part
|
mockRoots.forEach(function (id) {
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
expect(mockWorker.postMessage).toHaveBeenCalledWith({
|
||||||
// Array argument indicates multiple children
|
request: 'index',
|
||||||
mockCapabilityPromise.then.mostRecentCall.args[0]([]);
|
model: mockDomainObjects[id].getModel(),
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
id: id
|
||||||
// Call again, but for single child
|
});
|
||||||
mockCapabilityPromise.then.mostRecentCall.args[0]({});
|
});
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
|
|
||||||
expect(mockWorker.postMessage).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("when indexing, listens for composition changes", function () {
|
it("indexes members of composition", function () {
|
||||||
var mockListener = {composition: {}};
|
mockDomainObjects.root1.getModel.andReturn({
|
||||||
|
composition: ['a']
|
||||||
|
});
|
||||||
|
|
||||||
// Call indexItems
|
resolveAsyncTasks();
|
||||||
mockObjectPromise.then.mostRecentCall.args[0](mockDomainObjects);
|
resolveAsyncTasks();
|
||||||
|
|
||||||
// Call through listening for changes
|
expect(mockWorker.postMessage).toHaveBeenCalledWith({
|
||||||
expect(mockCapability.listen).toHaveBeenCalled();
|
request: 'index',
|
||||||
mockCapability.listen.mostRecentCall.args[0](mockListener);
|
model: mockDomainObjects.a.getModel(),
|
||||||
expect(mockObjectService.getObjects).toHaveBeenCalled();
|
id: 'a'
|
||||||
mockObjectPromise.then.mostRecentCall.args[0](mockDomainObjects);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("listens for changes to mutation", function () {
|
||||||
|
expect(mockMutationTopic.listen)
|
||||||
|
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
|
mockMutationTopic.listen.mostRecentCall
|
||||||
|
.args[0](mockDomainObjects.a);
|
||||||
|
|
||||||
|
resolveAsyncTasks();
|
||||||
|
|
||||||
|
expect(mockWorker.postMessage).toHaveBeenCalledWith({
|
||||||
|
request: 'index',
|
||||||
|
model: mockDomainObjects.a.getModel(),
|
||||||
|
id: mockDomainObjects.a.getId()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends search queries to the worker", function () {
|
it("sends search queries to the worker", function () {
|
||||||
@ -188,6 +266,28 @@ define(
|
|||||||
expect(mockDeferred.resolve).toHaveBeenCalled();
|
expect(mockDeferred.resolve).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("warns when objects are unavailable", function () {
|
||||||
|
resolveAsyncTasks();
|
||||||
|
expect(mockLog.warn).not.toHaveBeenCalled();
|
||||||
|
mockChainedPromise.then.mostRecentCall.args[0](
|
||||||
|
mockObjectPromise.then.mostRecentCall.args[1]()
|
||||||
|
);
|
||||||
|
expect(mockLog.warn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throttles the loading of objects to index", function () {
|
||||||
|
expect(mockObjectService.getObjects).not.toHaveBeenCalled();
|
||||||
|
resolveThrottledFn();
|
||||||
|
expect(mockObjectService.getObjects).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs when all objects have been processed", function () {
|
||||||
|
expect(mockLog.info).not.toHaveBeenCalled();
|
||||||
|
resolveAsyncTasks();
|
||||||
|
resolveThrottledFn();
|
||||||
|
expect(mockLog.info).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -110,20 +110,23 @@ define(
|
|||||||
* Get the latest telemetry datum for this domain object. This
|
* Get the latest telemetry datum for this domain object. This
|
||||||
* will be from real-time telemetry, unless an index is specified,
|
* will be from real-time telemetry, unless an index is specified,
|
||||||
* in which case it will be pulled from the historical telemetry
|
* in which case it will be pulled from the historical telemetry
|
||||||
* series at the specified index.
|
* series at the specified index. If there is no latest available
|
||||||
|
* datum, this will return undefined.
|
||||||
*
|
*
|
||||||
* @param {DomainObject} domainObject the object of interest
|
* @param {DomainObject} domainObject the object of interest
|
||||||
* @param {number} [index] the index of the data of interest
|
* @param {number} [index] the index of the data of interest
|
||||||
* @returns {TelemetryDatum} the most recent datum
|
* @returns {TelemetryDatum} the most recent datum
|
||||||
*/
|
*/
|
||||||
self.getDatum = function (telemetryObject, index) {
|
self.getDatum = function (telemetryObject, index) {
|
||||||
|
function makeNewDatum(series) {
|
||||||
|
return series ?
|
||||||
|
subscription.makeDatum(telemetryObject, series, index) :
|
||||||
|
undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return typeof index !== 'number' ?
|
return typeof index !== 'number' ?
|
||||||
subscription.getDatum(telemetryObject) :
|
subscription.getDatum(telemetryObject) :
|
||||||
subscription.makeDatum(
|
makeNewDatum(this.getSeries(telemetryObject));
|
||||||
telemetryObject,
|
|
||||||
this.getSeries(telemetryObject),
|
|
||||||
index
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
Reference in New Issue
Block a user