Compare commits

...

177 Commits

Author SHA1 Message Date
e73dcd8271 Only produce telemetry if window is full 2017-11-01 19:13:20 -07:00
ea3289b659 Merge branch 'telemetry-mean-historical' into demo-2017.1 2017-11-01 12:55:53 -07:00
0b2c0f6c80 Use formatters 2017-11-01 12:55:31 -07:00
90126ccc64 Implementing range and domain formatters 2017-11-01 12:31:43 -07:00
9a29ed8875 [API] get with keystring, mark methods not implemented
Update Object API such that get supports calls with either a keystring or an
identifier.

As save and delete are not implemented and  have different calling signatures,
implement them as separate methods so they can be documented separately.
2017-11-01 12:13:39 -07:00
ceb6c896ba Revert "Handle keyString or identifier"
This reverts commit b32f3b82dc.
2017-11-01 11:29:39 -07:00
b32f3b82dc Handle keyString or identifier 2017-11-01 11:13:06 -07:00
3601e6b473 Merge branch 'telemetry-mean-historical' into demo-2017.1 2017-11-01 10:38:09 -07:00
77a6bbdb76 Working historical telemetry filter 2017-11-01 10:37:13 -07:00
a8dcd5872c Bring back PlotViewPolicy 2017-10-31 12:18:33 -07:00
0c25d47e4a Fix for renamed view-control classes
Fixes #1795
Also removed .has-children from plot-options html files;
2017-10-31 11:29:30 -07:00
ffc81f1cd0 Simplified Telemetry Mean 2017-10-31 09:16:31 -07:00
ae1d1f0f90 Removed TelemetryMeanActionDecorator 2017-10-31 09:13:11 -07:00
7feb3fb18e Hard-code telemetry metadata 2017-10-31 09:00:43 -07:00
cd2c04aa4d Super sketchy mean telemetry plugin 2017-10-30 14:40:06 -07:00
3ac1f489c5 switch back to espresso 2017-10-30 14:33:06 -07:00
4ec2b6eb4d [Plot] First cut of new Plots
[Plot] Remove Old Plot

[Plot] Remove Telemetry Panel

[UTC] parse number is passthrough

[Telemetry] Support default arguments

[Plot] Include new plot

destroy is a model method

Cleanup models

send bounds and domain in telemetry request

pass value instead of trying to create datum

Fix Enum Formatting

Set max precision to avoid errors

Change plot series model management

Install plot by default

Make sure plots are always plots

Set default yKey on series

Use new time API

Update hints

proper id formats

Only redraw on change

Set x axis on bounds change

wip

Basic support for legend customization

Reasonable timeframe

[CSS] Generalized .view-control

Moved .view-control out of tree.scss and
generalized as a control prior to implementing
it as the expand/collapse control in plot legends
for #584 and #618.

[Frontend] New Styling for Plots

Large squish commit, messages as follows:

[Frontend] WIP style plot legends

Fixes #584
Fixes #618
Major layout changes to enable flex approach for
legend and plot display area; table styling for
expanded legend; legend on top, bottom, right
and left support added but still a bunch to do.

[Frontend] WIP style plot legends

Fixes #1590
Style and markup normalization for legend elements.

[Frontend] WIP style plot legends

Fixes #1590
Incremental progress.

[Frontend] WIP style plot legends

Fixes #1590
Legend layout enhancements.

[Frontend] WIP style plot legends

Fixes #1590
Significant mods to layout in Inspector while
editing, using new .compact-form class;
mod to ul.tree li to allow .compact-form layout
class within tree scope;

[Frontend] WIP style plot legends

Fixes #1590
Significant layout cleanup, mainly for stacked plots
and expanded legend;
Config options hide/show controls fully ported from
Pete's original version;

[Frontend] WIP style plot legends

Fixes #1590
New 12px crosshair glyph added;
'hover-value-enabled' markup and class refinements;

[Frontend] WIP style plot legends

Fixes #1590
Whoops - removed erroneous background-color
based on swatch color;

[Frontend] WIP style plot legends

Fixes #1590
Generalized .t-alert-unsynced, moved to _icons.scss;
Moved .t-object-alert to proper location in plot markup;
Added new .t-stacked-plot class;

[Frontend] WIP style plot legends

Fixes #1590
WIP styles for collapsed legend

[Frontend] WIP style plot legends

Fixes #1590
WIP styles for collapsed legend, major
progress

[Frontend] WIP style plot legends

Fixes #1590
WIP styles for collapsed legend, major
progress

[Frontend] WIP style plot legends

Fixes #1590
Legends v1 near complete, pre unit-testing

[Frontend] WIP style plot legends

Fixes #1590
Moved location of padding for .hover-value-enabled
elements

[Frontend] WIP style plot legends

Fixes #1590
Finessing of legend on side

[Frontend] WIP style plot legends

Fixes #1590
Sanding and shimming on legend overflow

[Frontend] WIP style plot legends

Fixes #1590
Added human-nice column labels for expanded legend

[Frontend] WIP style plot legends

Fixes #1590
Minor refactor to use explicit legend-collapsed and -expanded CSS
classes, replacing ng-show statements;
Removed ng-style that was setting column widths based on char
count of column name;

[Frontend] WIP style plot legends

Fixes #1590
Moved legend styles to own file;

[Frontend] WIP style plot legends

Fixes #1590
Cleaned up config error messaging; changed config to
default to collapsed legend by default;

[Frontend] WIP style plot legends

Fixes #1590
Sanding on side positions name/value display

[Frontend] WIP style plot legends

Fixes #1590
Global rename of `compact-form` to `inspector-config`;
Applied same to plot-options-browse.html for tree items;

[Frontend] WIP style plot legends

Fixes #1590
Renamed `plot-legend-on-*` selector;
Fixed layout for plot-legend-hidden, involved
returning `plot-wrapper-axis-and-display-area` to
use position: absolute;

[Frontend] WIP style plot legends

Fixes #1590
Minor tweak to cause hover values to wrap
in side-positioned legend.

[Frontend] WIP style plot legends

Fixes #1590
Removed abs positioning on `.plot-wrapper-axis-and-display-area`;
Removed ng-style to set height of stacked plot element, use
flex layout instead, fixes problem of bottom-most plot getting
clipped; polishing of hover display in collapsed legend;

[Frontend] WIP style plot legends

Fixes #1590
Removed commented dev markup

[Frontend] MIsc mods and fixes

Fixes #1590
Less brittle approach to 'niceValuesToShowWhenExpanded' array
that won't break when user config is wired up; mobile styling added
to hide nearest* columns in mobile (no hover) context; removed
hex color value display and use niceValuesToShowWhenExpanded
lookup in plot-options-browse.html; added CSS class 'comma-list';

handle missing request

Fix form options for plot

fix autoscale padding and custom ranges

proper expand by default handling

Show limit state in hover values

Draw special markers for points with limits

prevent unexpected range change when using autoscale

Don't floor coordinates

implement chart markers as options

Compatiblity series supports getData

Fallback when no limit evaluator present
2017-10-30 14:30:36 -07:00
1afecdc82c Trying to support CreateAction 2017-10-30 14:16:06 -07:00
b9cda6985e Only add (Mean) to range values 2017-10-30 11:28:18 -07:00
3a7e6ab7d5 Styles for NSS heatmap
Fixes #1791
Temporarily pulling in styles to main CSS;
(cherry picked from commit 38362b6)
2017-10-30 09:58:17 -07:00
401b7e3f19 Decorate PropertiesAction instead of persistence service to catch properties updates 2017-10-30 09:50:30 -07:00
971bcd1f71 Decorate PropertiesAction instead of persistence service to catch properties updates 2017-10-30 09:40:07 -07:00
c9e9845172 Telemetry mean function 2017-10-27 14:12:44 -07:00
abbba38eac Fix precision of displayed numberical telemetry to 2 DP 2017-10-27 09:14:39 -07:00
db856251da [Telemetry] Handle unspecified domains/ranges
Handle cases where domains and ranges are not set from
TelemetryMetadataManager; these properties will only be
present on legacy telemetry objects.

Encountered issue while developing heat map view using
example telemetry.

Supports goals for sprint Alice, https://github.com/nasa/openmct/projects/1
2017-10-24 14:54:39 -07:00
e1566e448d [Frontend] Kill iframe borders dead!
Fixes #1776
(cherry picked from commit 1685364)
2017-10-13 17:11:27 -07:00
d9aae0700c Merge branch 'master' into demo-2017.1 2017-10-13 10:28:06 -07:00
5c29726cc0 Merge branch 'demo-2017.1' of https://github.com/nasa/openmct into demo-2017.1 2017-10-04 15:16:33 -07:00
5c207c3fe0 Merge branch 'timeline-follow-1688' into demo-2017.1 2017-10-04 15:13:56 -07:00
eba1dd1a4e [Plugins] Match args to define string 2017-10-04 14:07:04 -07:00
570edc0dec Merge in fixes from no-frame-margin-1745 2017-10-04 10:28:29 -07:00
ad6bcd4ef8 [Frontend] Hide local controls for no-frame context
Fixes #1745
When a Layout or Fixed Position display is in a layout
with its frame hidden, also hide the hover buttons including
the View Large button.
2017-10-04 10:25:13 -07:00
aedbbbbf75 [Frontend] Remove border definition causing scrollbars
Fixes #1745
2017-10-04 09:44:36 -07:00
3caaf00483 Merge branch 'summary-widgets' into demo-2017.1 2017-10-04 09:23:46 -07:00
971eda4d88 [Frontend] Standardized font-size in widgets
Fixes #1668
2017-10-04 09:23:10 -07:00
090d216517 [Frontend] Changed no-frame margin to 0 from 2px
Fixes #1745
2017-10-03 16:47:53 -07:00
d42d7ae68d Merge remote-tracking branch 'origin/summary-widgets' into demo-2017.1 2017-10-03 14:28:32 -07:00
68e6e3c121 finalize changes for v1 and add/fix tests 2017-10-02 09:32:26 -07:00
7e7f39db2d Add tooltip values for duplicate and delete buttons
Fixes #1668
2017-09-28 15:36:47 -07:00
b6e0fca828 Standardize to sentence casing
Fixes #1668
2017-09-28 15:31:26 -07:00
ffc5896e5a Merge branch 'summary-widgets' of https://github.com/nasa/openmct into summary-widgets 2017-09-28 11:06:06 -07:00
fd6ebd152f fix and add appropriate tests 2017-09-28 11:05:42 -07:00
7a5c1c0e1f Clean up commented code
Fixes #1668
2017-09-28 10:35:38 -07:00
2f7e1e3f1a [Frontend] Added missing condition label text
Fixes #1668
Each condition in a rule beyond the first one
now displays the text label "or when" or "and when"
depending on the value of the user's selection in
the "Trigger when" control: "any" or "all"
2017-09-27 15:53:43 -07:00
d73746b51b [Frontend] Fix for tooltip not appearing
Fixes #1668
2017-09-27 14:49:00 -07:00
2df54af019 Merge branch 'summary-widgets' of https://github.com/nasa/openmct into summary-widgets 2017-09-27 11:23:57 -07:00
586269f761 remove js option from rules 2017-09-27 11:23:47 -07:00
e536ab34d7 [Frontend] Added l-flex-accordion class
Fixes #1668
2017-09-26 17:44:35 -07:00
e15002dd72 [Frontend] Added Rules expand/contract
Fixes #1668
2017-09-26 17:43:53 -07:00
453cf3ad6a watch for object changes and update url and newTab properties 2017-09-26 14:18:09 -07:00
5c46e48bde Merge lastest master, resolve conflicts in _menus.scss
Fixes #1668
2017-09-26 11:51:53 -07:00
868ea9362f make open in same tab as default option for url 2017-09-25 16:04:23 -07:00
d69106ff2c Merge remote-tracking branch 'origin/master' into summary-widgets
# Conflicts resolved:
#	platform/commonUI/general/res/sass/user-environ/_frame.scss
2017-09-25 12:33:42 -07:00
1658b17c56 add hyperlink functionality 2017-09-25 11:35:12 -07:00
39cf0528ca merge changes from charles 2017-09-25 10:04:44 -07:00
3d12f7312b checkstyle error fix 2017-09-25 10:04:25 -07:00
22481fdc31 [Frontend] Set default widget colors and icon
Fixes #1668
2017-09-22 12:05:32 -07:00
4a9d27dc79 [Frontend] Hover behaviors refined
Fixes #1668
2017-09-22 11:41:00 -07:00
a5a9fefd40 [Frontend] Significant mods to vertical-align for inputs
Fixes #1668
vertical-align: middle for selects, inputs; removed
commented code;
2017-09-22 11:40:06 -07:00
dae4074934 [Frontend] Various sanding and shimming
Fixes #1668
Added box-shadow to widget; better styles for
case where controls wrap;
2017-09-22 10:50:40 -07:00
a540a3573f [Front-end] WIP Styling for widget in layout
Fixes #1668
2017-09-21 15:30:12 -07:00
4e7fe9082c Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling-2 2017-09-21 15:26:47 -07:00
568141bf81 [Front-end] WIP Styling for widget in layout
Fixes #1668
2017-09-21 15:26:28 -07:00
ac3ea43fe5 hide equal to label until select field is available 2017-09-21 15:22:55 -07:00
e922e8d504 [Front-end] Add summary widgets to hide-buttons-in-Layout list
Fixes #1668
2017-09-21 15:11:15 -07:00
650a877d2a [Front-end] Inject icon directly into widget Label
Fixes #1668
Remove .widget-icon element;
2017-09-21 15:04:35 -07:00
1202109c59 Merge branch 'summary-widgets-styling-2' of https://github.com/nasa/openmct into summary-widgets 2017-09-21 14:53:27 -07:00
429d7bbd57 add hide/unhide to select.js 2017-09-21 14:53:06 -07:00
af749fe71b [Front-end] WIP for widget in Layout
Fixes #1668
Global rename of .l-widget-main to .w-summary-widget:
markup, SCSS and JS; styles; simplify widget markup;
2017-09-21 14:34:11 -07:00
cf64c512ce [Front-end] Tweaks
Fixes #1668
More fine-grained selector to display editing UI;
Tweak to "no-data" message content;
2017-09-21 13:48:48 -07:00
14592d1c3e [Timeline] Follow up on front-end updates
Fixes #1688

Squashed commit of the following:

commit 817c7f31289b3e7631c3332d2192a68f21f50f9e
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 21 12:47:48 2017 -0700

    [Timeline] Initialize lastWidth

    ...to avoid clamping values before a width has actually been observed.

commit 5f7324c1cdb0cbef6385fbccac31b0404d216f95
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 21 12:21:11 2017 -0700

    [Timeline] Clamp right edge of zoom

    ...to avoid getting stuck in a weird scrolling state for large
    timer values.

commit 076aca112392e65835e7a01ac8e28780d24bfff1
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 21 12:02:23 2017 -0700

    [Timeline] Don't set scroll.x to negative values

    ...avoids mispositioning timer-following line,
    https://github.com/nasa/openmct/issues/1688#issuecomment-330373625

commit ac9bdb919df69fac65b297487131e2c41204ebeb
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 21 11:32:49 2017 -0700

    [Timers] Loosen test expectation

    Resolves build failure https://circleci.com/gh/nasa/openmct/4181
    by reducing test specificity for indicator display name.
2017-09-21 12:48:33 -07:00
c5bd3da44a Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling-2 2017-09-21 11:39:04 -07:00
ff3e49e926 change composition policy to allow summary widgets to be added to folders, but not vice versa. Only allow telemetry producing objects to be added to summary widgets 2017-09-21 11:29:12 -07:00
e244a3e431 [Front-end] New null value selection strings
Fixes #1668
2017-09-21 11:23:26 -07:00
c5d9fb6fd9 [Front-end] Hide/show "no-data" message element
Fixes #1688
2017-09-21 11:11:12 -07:00
4c276ab422 [Front-end] Normalize font sizing in overlay
Fixes #1688
2017-09-21 10:26:47 -07:00
bf321abae4 Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling-2 2017-09-21 10:09:57 -07:00
7336968ef9 [Frontend] Add message markup and styling
Fixes #1688
Add to Summary Widgets UI;
2017-09-20 18:03:03 -07:00
d60956948b [Frontend] Sanding to avoid CSS collision
Fixes #1688
2017-09-20 18:02:26 -07:00
23d5c2e1ee [Frontend] Refactor messages markup and styling
Fixes #1688
Significant changes! Refactor to allow messages markup and CSS to be used
in Summary Widgets UI
2017-09-20 18:01:26 -07:00
2632b8891a Merge latest styling changes from Charles 2017-09-20 14:56:05 -07:00
fff4cd9d51 add s-status-no-data class to summary widgets parent div when no telemetry is added and remove when telemetry is added. Add a composition policy to only allow compatible object types to be added to summary widgets 2017-09-20 14:40:31 -07:00
7f9fd5c705 [Timers] Frontend updates for time-of-interest
Squashed commit of the following:

commit 370b910d36
Author: Charles Hacskaylo <charlesh88@gmail.com>
Date:   Wed Sep 20 10:59:00 2017 -0700

    [Frontend] Fix in FollowIndicator.js

    Fixes #1688

commit 883d1feb32
Author: Charles Hacskaylo <charlesh88@gmail.com>
Date:   Wed Sep 20 10:36:56 2017 -0700

    [Frontend] Styling and content on Follow indicator

    Fixes #1688

commit cff85fbbde
Author: Charles Hacskaylo <charlesh88@gmail.com>
Date:   Wed Sep 20 10:09:19 2017 -0700

    [Frontend] Styling complete on Follow Line

    Fixes #1688

commit 563a86b69f
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov>
Date:   Mon Sep 18 16:05:53 2017 -0700

    [Front-end] WIP Markup and CSS for Follow Line

    Fixes #1688
    Added line icon, style refinement;

commit fc49e5d023
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov>
Date:   Mon Sep 18 15:07:35 2017 -0700

    [Front-end] WIP Markup and CSS for Follow Line

    Fixes #1688
    Moved TimelineTOIController up 2 levels of markup hierarchy
    to allow Follow Lines, one in each split pane;
    Follow LInes markup and CSS in progress;

commit 8ec3c42291
Author: Charles Hacskaylo <charlesh88@gmail.com>
Date:   Wed Sep 13 16:46:14 2017 -0700

    [Frontend] WIP Timeline Follow Line

    Fixes #1688
    VERY WIP! Initial move of styles into classes;
2017-09-20 12:11:41 -07:00
be0291cf70 [Frontend] Removed t-condition from testDataItem
Fixes #1668
Fixes #1644
2017-09-19 17:51:16 -07:00
b3a6d7271d [Frontend] Mods to main styles
Fixes #1668
Fixes #1644
Added .sm to number inputs; removed padding
from .compact-form label; set inputs and selects
in .compact-form to use $btnStdH for height;
2017-09-19 17:39:12 -07:00
ebeed2f236 [Frontend] Significant styling for test data area
Fixes #1668
Fixes #1644
2017-09-19 17:37:15 -07:00
337c26c019 fix failing tests 2017-09-18 13:12:23 -07:00
730f363f94 Merge branch 'summary-widgets-styling-2' of https://github.com/nasa/openmct into summary-widgets 2017-09-18 09:44:15 -07:00
ae30e6110b merge from styling 2017-09-18 09:44:07 -07:00
6e4bf3e45b [Frontend] Styling, markup for test data area
Fixes #1668
Fixes #1644
Significant sanding and shimming, WIP;
2017-09-15 18:48:03 -07:00
5465ca92f9 [Frontend] Flex layout now working!
Fixes #1668
Fixes #1644
2017-09-15 17:33:28 -07:00
e55ea41b0a [Frontend] Align selects
Fixes #1668
Fixes #1644
2017-09-15 17:02:20 -07:00
8cfb3cc689 [Frontend] Merge latest master
Fixes #1668
Fixes #1644
2017-09-15 17:01:32 -07:00
2d728a1362 [Frontend] WIP Styling continued
Fixes #1668
Markup tweaks
2017-09-15 16:44:11 -07:00
99333988df [Frontend] WIP Styling continued
Fixes #1668
Fixes #1644:
VERY WIP! Outer wrapper styling; drag-n-drop
working currently; section-headers;
2017-09-15 16:43:01 -07:00
de783d4286 fix circle-ci build errors 2017-09-14 14:11:02 -07:00
1e1a2443d5 [Timers] Remove unused variable to pass lint checks 2017-09-11 11:24:52 -07:00
d65e1e604e Squashed commit of the following:
commit f1dc1ce152e186da0d10c8e77d920ac0a76c9bc2
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:35:38 2017 -0700

    [Timers] Rewrite JSDoc for FollowTimerAction

    https://github.com/nasa/openmct/pull/1694/files#r137604769

commit 7ab0693cc983f8a04ac8ee9002f4d776b06a869a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:27:53 2017 -0700

    [Timer] Expect domain objects from FollowIndicator test

commit ff89c0849d16ab451bfd2fddd9202cf36940f599
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:26:28 2017 -0700

    [Timer] Add JSDoc for new method

commit 2a0343352eca241dfc28a4aa0b3832e3e6928864
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:24:59 2017 -0700

    [Timeline] Update TOI tests

    ...to account for refactoring out of tick handling.

commit 01cbaafc72870fab4ada5894637ae5721214933d
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:17:25 2017 -0700

    [Timeline] Update dependencies for TOI test

commit 6bd5c378566362dce331e7c200dea87f0b08ecc6
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:15:21 2017 -0700

    [Timers] Update timerService tests with dependencies

commit b0793865c5131e17a58786ec356d67f2f2bba4c5
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:09:54 2017 -0700

    [Timers] Declare vars to satisfy JSHint

commit 9d2a63f7fe61dadf68255d795512ec55f532c533
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 14:07:12 2017 -0700

    [Timeline] Handle stopped timer

commit 30871270514730f3f2f12482075e5140bb97fa1f
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 13:59:08 2017 -0700

    [Timer] Tweak refactored timer logic

commit 53ad127ba7cf679377dc865301612a1d78399324
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 13:53:36 2017 -0700

    [Timer] Convert times from timerService

    ...to reduce resposibilities for TOI controller.

commit f8341133cf23df383b8f6e4815b88e0066ebd2bc
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 13:03:37 2017 -0700

    [Timeline] Factor out timer knowledge

commit aebd9e0ac223971b868b03343dbe4c61c6eb4849
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 12:34:58 2017 -0700

    [Timeline] Consistently use this

commit 48ac427a20c5c343aecdbd54b068d8691f7830b6
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 12:33:57 2017 -0700

    [Timeline] Remove unused tick binding/call

commit ea62f0a15ba4ab5de53213bbed14599eaf878d70
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 12:10:59 2017 -0700

    [Timeline] Retrieve timestamp on demand

commit f53bd04b5e343b22ea52b431785ade891577bb6a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 12:07:55 2017 -0700

    [Timeline] Update clocks on bounds events

    https://github.com/nasa/openmct/pull/1694/files#r137603081

commit 51d8e376ee46aafa13cd9a969c6f03885e10dafb
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 11:40:33 2017 -0700

    [Timeline] Don't listen for non-existing tick events

commit 5cc40c488cec5e7453c2fe1dea5e5a4fa3509ecd
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 11:39:21 2017 -0700

    [Time] Revert Time API changes

    https://github.com/nasa/openmct/pull/1694/files#r137603081

commit c55c8bc627bf0a7f3cfd04b604b82d15ff469ab9
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 11:37:40 2017 -0700

    [Timeline] Finish testing TOI controller

commit af5cea5f2f172a309568d477dfdf11b8d45e74bb
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 11:06:47 2017 -0700

    [Timeline] Test TOI controller

commit ba64db68b132fa431e8ccdb533024bf2850f9712
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 10:06:41 2017 -0700

    [Timers] Test timerService

commit 247e663b326ec5b8145b832af9b26086204baea3
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 10:05:24 2017 -0700

    [Timers] Remove unused timerService method

commit 8d741ad5744e1b7deb669dbaa0f3d30e4eb5866e
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 09:59:32 2017 -0700

    [Timers] Remove unused timerService dependency

commit b59c8917bdef5ec3e54c8857d993d86547cfe177
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 09:58:10 2017 -0700

    [Timers] Remove unused timerService event

commit f15dd9827f835a814dc40a6201c90268a60ed64a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 09:49:09 2017 -0700

    [Timers] Test timer-following indicator

commit 2501f11af8c0b2aed9ebf16ffd28c0003b2701c2
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 09:42:54 2017 -0700

    [Timer] Complete test coverage for FollowTimerAction

commit aa2be83fc15cd68ee6de4d9f8205dc2fcba8c35b
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Thu Sep 7 09:35:37 2017 -0700

    [Timers] Begin testing Follow Timer action

commit d9062e0b0ff351b141dcb646972053ec72292d53
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 16:45:18 2017 -0700

    [Timeline] Remove unused variables

commit 79ebe4dd2b2aefc1e83ea8142588ed0715b3c269
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 16:39:22 2017 -0700

    [Timeline] JSDoc for TOI controller

commit 330f6b465188555e8e59f4eaf8ce1875b5335846
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 16:30:58 2017 -0700

    [Timeline] Use different icon to follow time bounds

commit f0a3b628e6d1d843324085edd563b68997f5a215
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 16:30:46 2017 -0700

    [Timeline] Simplify TOI following initialization

commit e76f3d1d525e0d19845b4c5b457995e60c416ad0
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 16:27:07 2017 -0700

    [Timeline] Add toggle to follow time bounds

commit 8ec072c0a2a953c074e0c327430dd68f27894ffb
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 16:19:25 2017 -0700

    [Timeline] Follow TC bounds based on boolean

commit 206a26734dedc267af6d298a77658aa261ca4fea
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 15:37:12 2017 -0700

    [Timeline] Tune bounds following

commit 19563bdf53a036c7bf09c52924425a1902b243bb
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 15:19:19 2017 -0700

    [Timeline] Remove unused method

commit 293981ec55ad115d7bd90b92f5bd090df64bd7c2
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 15:18:59 2017 -0700

    [Timeline] Only update timestamp on tick

    Leave bounds-following to the bounds event

commit 9180e15971d2043f0999a16f0aa8794273bcfc74
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 14:43:01 2017 -0700

    [Time] Document tick event

commit c7b163dff0d94aaea86b76647501f21623b353e1
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 14:39:57 2017 -0700

    [Timeline] Stop listening on destroy

    ...from the TOI controller.

commit ca7def3cf98e1eaf6c3aeb16cf9fd79452c86bd0
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 14:32:40 2017 -0700

    [Timeline] Remove surplus watches

commit 367e7afa94ae1ed448e39f13be804c62e2bfcf00
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 14:30:14 2017 -0700

    [Timeline] Very deltas are valid before panning

commit 7ee94f316e90d046015266a2a9168e349ff73345
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 14:28:10 2017 -0700

    [Timeline] Scroll with TOI only while in view

commit 9d7bb431119b7bc6ddc86f0058718e4385478518
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 10:36:46 2017 -0700

    [Timeline] Utilize zoomController.bounds

commit f151b9e8adfd235c31e32bef5fddab835efa7c8f
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 10:35:57 2017 -0700

    [Timeline] Add methods to set zoom bounds

commit c3d0b9876ab79c18003838ed3315045c5fb2ddbb
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 10:32:08 2017 -0700

    [Timelines] Observe bounds changes

    ...to synchronize zoom with Time Conductor, #1688

commit 58adafc46f231b0fd92827d10c377131166ff39c
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 09:37:07 2017 -0700

    [Timers] JSDoc for TimerService

commit a325a8d5085bf1a4c9aa3ab20771308d4789765a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 09:12:50 2017 -0700

    [Timeline] Re-tweak follow scroll calculations

    ...for visibility.

commit 41e4bf153607b081aaf92253fa2b21300e2f0ea7
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 09:03:45 2017 -0700

    [Timeline] Tweak follow scroll calculations

    ...for visibility.

commit 08a5b9f14ab629a310dc27a3771ca454f1187327
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 08:59:45 2017 -0700

    [Timeline] Replace debug output with scroll updates

commit 26585ecd61341b4ee89abd8ee866e705a02bbc9a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 08:59:07 2017 -0700

    [Timeline] Move TOI to scrollable area

commit 654eda027c3c67a3a0ff33136109ca27d14762ba
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 08:56:07 2017 -0700

    [Timeline] Begin implementing TOI following

commit 552f67a11ce439be58ab7ca7884c46241a25adee
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Sep 6 08:55:51 2017 -0700

    [Timeline] At zoom-to-time method

    For use by time-of-interest controller, #1688

commit 37acbfd458740b2c3176875f83d37f0fdf57e727
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 12:46:33 2017 -0700

    [Timeline] Remove other excess $apply calls

    ...although this should make us nervy about those callbacks being
    invoked in different ways.

commit 0e72847c9ba59f957efa2d412cb77c024afa9e63
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 12:44:27 2017 -0700

    [Timeline] Remove $apply from $watch callback

    ...to avoid an infinite digest loop.

commit bade0fd9f60101d5b1b782cd28e608af493c9076
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 12:42:18 2017 -0700

    [Timeline] Begin adding TOI line to template

commit f94034a3b4136f6b174155397084f8cdb22ce544
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 12:11:25 2017 -0700

    [Timers] Add missing semicolon, satisfy JSHint

commit cb465b94011e7432cc7e4d9e815641f97dc61d7a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 12:08:45 2017 -0700

    [Time] Verify that tick event is emitted

commit 7c84a86a33ceb73ba6a06801374ea3f89793c450
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 11:59:06 2017 -0700

    [Time] Emit tick events from Time API

    https://github.com/nasa/openmct/pull/1694

commit d319a783fcd882c03eb7d9a81fec33898016384e
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Wed Aug 30 11:56:28 2017 -0700

    [Timeline] Sketch in TOI controller

    ...to position/follow time-of-interest, relative to the active timer.

commit 2dbdb2627450039d69dbfd10eed2c100207e061a
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 12:57:47 2017 -0700

    [Timers] Use timerService

    ...to coordinate between action and indicator

commit f94a2358eaf0366bd4da2b44e69ccb62b153c5db
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 12:52:22 2017 -0700

    [Timers] Use TimerService from Follow Timer action

commit a720c2ec2cda4a300d26167f4717f0571bedcbfd
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 12:50:31 2017 -0700

    [Timers] Expose TimerService through bundle

commit e32bbc3e232d25f7c5dba98674781e4f263c4870
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 12:49:03 2017 -0700

    [Timers] Sketch in timer service

    ...which will keep track of the active timer used to interpret SET
    for Timelines.

commit a038c2b1d8fd34c2874fa8fc0421fa7ba53e11ab
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 12:41:05 2017 -0700

    [Timers] Register indicator

commit 0e93ae87a1cccc4f3a0636844625b64ccb77a7ae
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 12:39:21 2017 -0700

    [Timers] Skeleton for time following indicator

commit e806386891639740e9fe3d8641c2f60ab5a88eac
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 09:37:14 2017 -0700

    [Timers] Register the Follow Timer action

commit 008aa95932070459dcc6fa1d918a23dac8df7592
Author: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date:   Tue Aug 29 09:35:08 2017 -0700

    [Timers] Skeleton for Follow Timer action

    ...to synchronize the time conductor with a particular Timer. #1688
2017-09-07 15:22:38 -07:00
f6c1488ccd [Summary Widgets] Merge with style
Merge remote-tracking branch 'origin/summary-widget-styling-2' into summary-widgets
2017-08-25 14:23:06 -07:00
26be1ecf37 [Summary Widgets] Merge with style
Merge remote-tracking branch 'origin/summary-widget-styling-2' into summary-widgets

Restore correct glyphs after merge with master
2017-08-25 14:19:50 -07:00
38f0f072bb [Merge] Ellipsize added to l-summary-widget
Fixes #1669
2017-08-25 14:19:43 -07:00
e5e969665f [Merge] Merge latest master
Fixes #1669
Merge + resolve conflicts;
2017-08-25 14:11:34 -07:00
ffbb662c99 [Summary Widgets] Unit tests 2017-08-25 14:09:22 -07:00
bd7b23f896 [Summary Widgets] Merge from style branch
Merge remote-tracking branch 'origin/summary-widget-styling-2' into summary-widgets

Resolve conflicts with divergent rule implementation
2017-08-25 12:43:29 -07:00
c238def902 [Summary Widgets] Unit tests 2017-08-25 11:57:29 -07:00
2d430ece7f [Merge] Thumbs now show icon and label text
Fixes #1669
Updates to allow thumbs to show their associated
icon and label text;
minor CSS tweaks for icon size and ellipsizing;
New rules now don't use first icon by default;
2017-08-25 11:40:46 -07:00
c92644a661 [Summary Widgets] Merge from master
Merge remote-tracking branch 'origin/master' into summary-widgets

Patch error with SineWave generators after merge
2017-08-25 10:04:32 -07:00
41ce3c04f7 [Summary Widgets] Unit Tests 2017-08-24 16:52:44 -07:00
fcf77f359f [Summary Widgets] Unit tests 2017-08-24 12:06:31 -07:00
40a2737915 [Summary Widgets] Performance Improvements
Remove unecessary re-initialization of Rule objects on certain
user interactions, and cleanup related code
2017-08-24 10:12:55 -07:00
216489d67f [Summary Widgets] Tests
Add unit tests
2017-08-23 17:01:14 -07:00
418a393b26 [Sumamry Widgets] Destroy
Implement destroy
2017-08-23 13:09:36 -07:00
1f3d744494 [Summary Widgets] Event Emitters
Replace custom event handlers with EventEmitters, remove binds
assoicated with old event handlers, and update documentation and
callback functions
2017-08-22 23:31:45 -07:00
ff3f2dccba [Summary Widget] Tests
Add and update unit tests

Fix style in markup

Begin implementing destroy
2017-08-22 16:51:00 -07:00
e69973bd29 [Summary Widgets] Documentation
Finished adding JSDoc comments

Standardize usage of event handler callbacks

Fix rule persistence bug
2017-08-22 11:13:26 -07:00
05b352cc36 [Summary Widgets] Documentation
Add JSDoc style comments
2017-08-21 17:05:41 -07:00
9735548999 [Summary Widgets] Add documentation 2017-08-18 16:22:23 -07:00
f9529b1362 [Summary Widgets] UI for scripted conditions
Add textfield input for JavaScript condition input, and modify the
condition manager and condition evaluator to read JS conditions
correctly from the data model. Currently, custom conditions are
not actually executed and will always be evaluated as false.
2017-08-18 14:27:57 -07:00
c598cec702 [Summary Widgets] Unit Tests
Add and update tests for select classes

Stub specfiles for test data and DnD
2017-08-17 16:44:33 -07:00
e9ea1c4a0f [Summary Widgets] Edit Mode
Enable edit mode for summary widgets, and make configuration interface
visible only when the user has entered edit mode

Standardize usage of 'mutate'

Fix collision between widget palettes and other interfaces where
palettes would permanently hide other menus
2017-08-17 13:47:49 -07:00
e9238ff282 [Summary Widgets] Merge from View API Branch
Merge remote-tracking branch 'origin/view-api-implementation'
 into summary-widgets
2017-08-17 09:31:36 -07:00
4cebd72cba [Summary Widgets] Merge from style branch
Issues #1644 #1669

Merge remote-tracking branch 'origin/summary-widget-styling-2' into summary-widgets

Integrate new style for inputs
2017-08-16 14:57:29 -07:00
f8a44d6e71 [Summary Widget] Test Data
Issue #1644

Add user-configurable mock data to test rules. Modify evaluator to
gracefully handle uninitialzed test data points.

Fix DnD bug where drag position was not tracked correctly
2017-08-16 14:37:03 -07:00
d0745b300b Allow views to be editable 2017-08-16 11:20:04 -07:00
2a4e0a3081 [Frontend] Mods to custom selects
Fixes #1669
line-height, context arrow positioning and color
2017-08-16 10:50:08 -07:00
1ff19f9574 [Front-end] Refinements to palette .selected
Fixes #1669
Unit tested in espresso and snow themes as well;
2017-08-15 16:04:07 -07:00
7ef84cb50d [Front-end] Fix palette menu when no icon is selected
Fixes #1669
2017-08-15 15:51:32 -07:00
cd05c70d64 [Front-end] Fixes to palette CSS
Fixes #1669
Changed .selected to avoid icon collision when
applying .selected to a an item in an icon palette;
Moved color defs into theme constants files;
TO-DO: fix custom select alignment issues,
fix menu when no icon is selected,
unit test in Snow theme.
2017-08-15 15:24:18 -07:00
568473b82f [Summary Widgets] Rule Reorders
Cleanup Widget DnD code and package it as a module
2017-08-14 16:24:38 -07:00
c61b074755 [Summary Widgets] Rule Reorders
Re-implement drag and drop with a 'sortable list' style interface
2017-08-14 14:21:37 -07:00
8ed66ab4ab [Summary Widgets] Rule Reorders
Implement drag and drop rule reorders using the native HTML5 API
2017-08-11 16:57:40 -07:00
b2502dd998 [Summary Widgets] Selection classes for palettes
Merge remote-tracking branch 'origin/summary-widget-styling-2' into
 summary-widgets. Issues #1669 #1644

Update palette classes to apply a visual indicator for selection
to their items.
2017-08-10 10:39:15 -07:00
856eedbf9d [Summary Widgets] 'Any/All Telemetry' in conditions
Add UI and implemenetion for evaluating any telemetry or all telemetry
in an individual condition. Add related unit tests.
2017-08-09 16:54:19 -07:00
0c0ca6e6af [Frontend] Colors for palette defined
Fixes #1669
2017-08-09 16:00:33 -07:00
498b797e49 [Summary Widgets] Fix input issue
Fix a bug in the compsosition object selector where it would not display its
currently selected item on a composition add.

Update names of modules to more accurately describe their function.
2017-08-09 10:44:41 -07:00
02c33388ba [Summary Widgets] Generate Rule Descriptions
Dynamically update the rule description based on the current state
of the rules' conditions
2017-08-08 17:47:54 -07:00
8a8e3cc055 [Front-end] WIP colors for Summary Widget palettes
Fixes #1669
2017-08-08 16:11:52 -07:00
36d60b16e9 [Front-end] Updated Style Guide
Fixes #1669
Updated palette example in Style Guide to use `no-selection` and `selected` classes;
2017-08-08 15:35:41 -07:00
de3114568b [Front-end] Implemented no-selection class in widgets
Fixes #1669
Added `no-selection` class to "None" selection choice in widgets palette;
2017-08-08 15:28:48 -07:00
eb5835faeb [Summary Widgets] Fix color palettes
Fix color palette bug where the 'none' option was not recognized as
a selectable item

Add ability to toggle 'none' option, and remove it from the text color
control

Remove 'grippy' element when only one user-defined rule exists

Fix format of requirejs headers

Add tooltips
2017-08-08 15:27:46 -07:00
ff1ddb0b79 [Front-end] Added .selected class for palette items
Fixes #1669
Added `.selected` in `.s-palette-item` class;
Re-orged styles in palette.scss to place in .l-* and .s-*
classes properly;
2017-08-08 15:16:20 -07:00
15b127bb2e [Front-end] Added 'no-selection' CSS class
Fixes #1669
Added to _global.scss;
implemented in color.html;
2017-08-08 15:09:56 -07:00
e4ed881f6d [Summary Widgets] Code Style
Fix code style issues

Update plugin syntax to more closely match existing plugins
2017-08-08 12:06:20 -07:00
7b62cf130c [Summary Widget] Add unit tests
Add tests for input classes

Fix code style errors
2017-08-07 17:07:21 -07:00
72fd2e531c [Summary Widget] Assorted Cleanup
Abstract palette behavior to superclass, and base new color and icon
palette classes on it

Add unit tests

Move private event handler methods out of object prototypes
2017-08-04 16:15:47 -07:00
4a5392ef78 [Summary Widgets] Minor Fixes
Update zepto in bower to enforce v1.2.0

Fix bug where summary widget conditions would modify appearance of other summary widgets

Move code to plugins directory

Stub for unit tests
2017-08-03 13:09:05 -07:00
0150a708ca [Evaluation] Implement condition evaluation
Issue #1644

Add telemetry subscription handling, and implement the execution of
rules.

Provide a toggle for 'any', 'all', or 'JavaScript' trigger

Add documentation
2017-08-02 14:31:26 -07:00
eacc181d5e [Inputs] Add value inputs for rule configuration
Dynamically add value inputs when an operation is being configured.

Cleanup code related to removed label caching feature
2017-08-01 16:03:08 -07:00
405bb55881 [Refactor] Cleanup old files 2017-08-01 13:51:20 -07:00
4a35508459 [Refactor] Overhaul of code structure
Re-implement widgets in with a module-oriented structure. Fix issues related to
asychronous loading of telemetry and composition.

Package inputs as re-usable, Zepto-based modules.
2017-08-01 13:40:04 -07:00
98a9d71a2e [Summary Widgets] Implementation for conditions
Support configuring and persisting multiple conditions per rule
2017-07-26 09:33:27 -07:00
a1596d0b06 [Summary Widgets] Make conditions persistable
Issue #1644

Add implementation and persistability for a single condition configuration
field. Baseline for adding arbitrary numbers of rules
2017-07-25 14:21:53 -07:00
4b3be4c483 [Inputs] Add implementation for icon palette
Issue #1644

Wire up icon palette inputs to widget, and make icon class a persistable
property of a rule
2017-07-24 12:15:39 -07:00
0fa8472db1 [Bug Fix] Fix text input handlers
Issue #1644

Fix merge issue related to inputs for label and message
2017-07-24 10:10:28 -07:00
e1e2dca1d8 [Frontend] WIP summary widget styling
Fixes #1654
Refactor .l-color-palette to .l-palette, includes
file renaming; add icon-palette in WidgetView.js
2017-07-21 18:07:49 -07:00
755c013ec8 [Summary Widgets] Merge with style
Issue #1644

Merge and resolve conflicts with style branch

Fix rule indexing bug
2017-07-21 16:19:15 -07:00
eab702b763 [Summary Widgets] Link markup to implementation
Add additional implemenation for new markup. Rules now store label
and message, and label is applied to widget. Implementation for
duplicate.
2017-07-21 15:59:53 -07:00
d15446ac91 [Frontend] WIP summary widget styling
Fixes #1654
Mod .grippy to point at new glyph class;
Stubbed in icon palette control in markup;
2017-07-21 10:32:20 -07:00
500733afb2 [Frontend] Add new icon-grippy
Fixes #1654
2017-07-21 10:25:18 -07:00
2aa04b0a56 Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling 2017-07-21 10:04:23 -07:00
c051f342af [Style] Merge with style branch
Merge remote-tracking branch 'origin/summary-widgets-styling' into summary-widgets

Apply new style and wire up new markup for interaction
2017-07-21 09:58:23 -07:00
8aeb365f5f [Frontend] WIP summary widget styling
Fixes #1654
Add condition duplicate and delete buttons
2017-07-21 09:56:57 -07:00
827a28313d [Frontend] WIP summary widget styling
Fixes #1654
Fixes and tweaks post-merge
2017-07-21 09:48:54 -07:00
c83de8aad2 [Summary Widgets] Minor UI implementation 2017-07-21 09:24:27 -07:00
b55f43b8df [Frontend] WIP summary widget styling
Fixes #1654
Merge latest from summary-widgets branch
2017-07-21 09:15:19 -07:00
8466723a90 [Frontend] WIP summary widget styling
Fixes #1654
Minor tweaks; class renaming; restructured
main containers in ruleTemplate.html;
minor updates for class names in WidgetView.js;
2017-07-20 19:01:13 -07:00
a103b4dbff [Frontend] WIP summary widget styling
Fixes #1654
Significant mods to markup and SCSS;
2017-07-20 17:56:57 -07:00
826ac3a947 [Frontend] WIP summary widget styling
Fixes #1654
Ported .view-control SCSS out of tree context to
allow more independent use;
2017-07-20 14:31:59 -07:00
597327f138 [Frontend] WIP summary widget styling
Fixes #1654
New SCSS for widgets; markup mods in progress
2017-07-20 14:09:17 -07:00
bef79402ca [Frontend] WIP summary widget styling
Fixes #1654
Ported .inspector-config SCSS from _inspector.scss
and renamed to .l-compact-form;
2017-07-20 14:08:49 -07:00
e68e0c381f [Summary Widgets] Make Rules Deletable
Add delete functionality and UI

Improve process for indexing and rendering rules to support deletion,
and eventually reordering

Fix bug where default style was passed by reference and overwritten
on style updates
2017-07-19 17:38:17 -07:00
c73f7259c2 [Frontend] WIP summary widget styling
Fixes #1654
Markup cleanups
2017-07-19 15:38:44 -07:00
4c9235ba10 [Frontend] Apply new summary-widget glyph
Fixes #1654
2017-07-19 15:01:04 -07:00
55e2a77df8 [Frontend] Add new summary-widget glyph
Fixes #1654
Updated font files and icomoon json file;
2017-07-19 15:00:46 -07:00
cfbff02e7f [Summary Widgets] Populate rule config inputs
Add rule configuration inputs, populated with domain objects, metadata,
and appropriate operations for a given type
2017-07-18 16:19:46 -07:00
54980fb296 [Summary Widgets] Add summary widgets, WIP
Add a summary widget domain object type

Implement basic interface and style configuration for rules
2017-07-18 10:58:55 -07:00
ba98d9315c [ViewAPI] Update view API with more support
Update view provider to allow metadata definitions and to play
nicely with old style views.

Spec out some updates to ViewProviders and ViewRegistry to
support further use of views.
2017-07-05 13:56:32 -07:00
208 changed files with 13280 additions and 8480 deletions

View File

@ -18,7 +18,7 @@
"node-uuid": "^1.4.7",
"comma-separated-values": "^3.6.4",
"FileSaver.js": "^0.0.2",
"zepto": "^1.1.6",
"zepto": "1.2.0",
"eventemitter3": "^1.2.0",
"lodash": "3.10.1",
"almond": "~0.3.2",

View File

@ -53,13 +53,15 @@ define([
'dataRateInHz'
];
request = request || {};
var workerRequest = {};
props.forEach(function (prop) {
if (domainObject.telemetry && domainObject.telemetry.hasOwnProperty(prop)) {
workerRequest[prop] = domainObject.telemetry[prop];
}
if (request.hasOwnProperty(prop)) {
if (request && request.hasOwnProperty(prop)) {
workerRequest[prop] = request[prop];
}
if (!workerRequest[prop]) {

View File

@ -121,7 +121,7 @@
<h2>Palettes</h2>
<div class="cols cols1-1">
<div class="col">
<p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one.</p>
<p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one. Selected palette choices should utilize the <code>selected</code> CSS class to visualize indicate that state.</p>
<p>Note that while this example uses static markup for illustrative purposes, don't do this - use a front-end framework with repeaters to build the color choices.</p>
</div>
<mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
@ -129,9 +129,9 @@
<div class="s-button s-menu-button menu-element t-color-palette icon-paint-bucket" ng-controller="ClickAwayController as toggle">
<span class="l-click-area" ng-click="toggle.toggle()"></span>
<span class="color-swatch" style="background: rgb(255, 0, 0);"></span>
<div class="menu l-color-palette" ng-show="toggle.isActive()">
<div class="menu l-palette l-color-palette" ng-show="toggle.isActive()">
<div class="l-palette-row l-option-row">
<div class="l-palette-item s-palette-item " ng-click="ngModel[field] = 'transparent'"></div>
<div class="l-palette-item s-palette-item no-selection"></div>
<span class="l-palette-item-label">None</span>
</div>
<div class="l-palette-row">
@ -147,7 +147,7 @@
<div class="l-palette-item s-palette-item" style="background: rgb(255, 255, 255);"></div>
</div>
<div class="l-palette-row">
<div class="l-palette-item s-palette-item" style="background: rgb(136, 32, 32);"></div>
<div class="l-palette-item s-palette-item selected" style="background: rgb(255, 0, 0);"></div>
<div class="l-palette-item s-palette-item" style="background: rgb(224, 64, 64);"></div>
<div class="l-palette-item s-palette-item" style="background: rgb(240, 160, 72);"></div>
<div class="l-palette-item s-palette-item" style="background: rgb(255, 248, 96);"></div>

View File

@ -25,8 +25,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title></title>
<script src="bower_components/requirejs/require.js">
</script>
<script src="bower_components/requirejs/require.js"> </script>
<script>
var THIRTY_MINUTES = 30 * 60 * 1000;
@ -44,13 +43,14 @@
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.TelemetryMean());
openmct.install(openmct.plugins.Conductor({
menuOptions: [
{
name: "Fixed",
timeSystem: 'utc',
bounds: {
start: Date.now() - 30 * 60 * 1000,
start: Date.now() - THIRTY_MINUTES,
end: Date.now()
}
},
@ -65,6 +65,7 @@
}
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<div class="abs top-bar">
<div class="title">{{ngModel.title}}</div>
<div class="dialog-title">{{ngModel.title}}</div>
<div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
</div>
<div class='abs editor'>

View File

@ -1,11 +1,10 @@
<div class="l-message"
ng-class="'message-severity-' + ngModel.severity">
<div class="ui-symbol type-icon message-type"></div>
<div class="message-contents">
<div class="w-message-contents">
<div class="top-bar">
<div class="title">{{ngModel.title}}</div>
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
</div>
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
<div class="message-body">
<div class="message-action">
{{ngModel.actionText}}
@ -25,8 +24,6 @@
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</a>
</div>
</div>
</div>

View File

@ -1,17 +1,17 @@
<mct-container key="overlay" class="t-message-list">
<div class="message-contents">
<div class="abs top-bar">
<div class="title">{{ngModel.dialog.title}}</div>
<mct-container key="overlay">
<div class="t-message-list">
<div class="top-bar">
<div class="dialog-title">{{ngModel.dialog.title}}</div>
<div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 ||
ngModel.dialog.messages.length == 0">s</span>
</div>
</div>
<div class="abs message-body">
<div class="w-messages">
<mct-include
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
key="'message'" ng-model="msg.model"></mct-include>
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
key="'message'" ng-model="msg.model"></mct-include>
</div>
<div class="abs bottom-bar">
<div class="bottom-bar">
<a ng-repeat="dialogAction in ngModel.dialog.actions"
class="s-button major"
ng-click="dialogAction.action()">

View File

@ -21,7 +21,7 @@
-->
<mct-container key="overlay">
<div class="abs top-bar">
<div class="title">{{ngModel.dialog.title}}</div>
<div class="dialog-title">{{ngModel.dialog.title}}</div>
<div class="hint">{{ngModel.dialog.hint}}</div>
</div>
<div class='abs editor'>

View File

@ -121,6 +121,9 @@ define([
};
UTCTimeFormat.prototype.parse = function (text) {
if (typeof text === 'number') {
return text;
}
return moment.utc(text, DATE_FORMATS).valueOf();
};

View File

@ -137,6 +137,11 @@
min-height: 0;
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
}
&.l-flex-accordion .flex-accordion-holder {
display: flex;
flex-direction: column;
//overflow: hidden !important;
}
.flex-container { @include flex-direction(column); }
}

View File

@ -99,7 +99,7 @@ $plotXBarH: 32px;
$plotLegendH: 20px;
$plotSwatchD: 8px;
// 1: Top, 2: right, 3: bottom, 4: left
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW);
/* min plot height is based on user testing to find minimum useful height */
$plotMinH: 95px;
/*************** Bubbles */

View File

@ -40,7 +40,7 @@
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
* to generate font files
*/
font-family: 'symbolsfont 12px';
font-family: 'symbolsfont-12px';
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot');
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
@ -180,6 +180,20 @@ a.disabled {
@include ellipsize();
}
.no-selection {
// aka selection = "None". Used in palettes and their menu buttons.
$c: red; $s: 48%; $e: 52%;
@include background-image(linear-gradient(-45deg,
transparent $s - 5%,
$c $s,
$c $e,
transparent $e + 5%
));
background-repeat: no-repeat;
background-size: contain;
}
.scrolling,
.scroll {
overflow: auto;
@ -234,6 +248,12 @@ a.disabled {
color: rgba(#fff, 0.2);
}
.comma-list span {
&:not(:first-child) {
&:before { content: ', '; }
}
}
.test-stripes {
@include bgDiagonalStripes();
}

View File

@ -44,6 +44,12 @@
}
}
.t-alert-unsynced {
@extend .icon-alert-triangle;
color: $colorPausedBg;
}
.bar .ui-symbol {
display: inline-block;
}
@ -81,18 +87,5 @@
@include transform(scale(0.3));
z-index: 2;
}
/* .t-item-icon-glyph {
&:after {
color: $colorIconLink;
content: '\e921'; //$glyph-icon-link;
height: auto; width: auto;
position: absolute;
left: 0; top: 0; right: 0; bottom: 20%;
@include transform-origin(bottom left);
@include transform(scale(0.3));
z-index: 2;
}
}*/
}
}

View File

@ -26,5 +26,6 @@
display: block;
height: 100%;
width: 100%;
border: none;
}
}

View File

@ -53,6 +53,7 @@
.l-inspector-part {
box-sizing: border-box;
padding-right: $interiorMargin;
.tree .form {
margin-left: $treeVCW + $interiorMarginLg;
}
@ -78,6 +79,7 @@
}
}
.form-row {
// To be replaced with .inspector-config, see below.
@include align-items(center);
border: none !important;
margin-bottom: 0 !important;
@ -99,15 +101,12 @@
position: relative;
}
ul li {
margin-bottom: $interiorMarginLg;
}
em.t-inspector-part-header {
border-radius: $basicCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
margin-bottom: $interiorMargin;
margin-top: $interiorMarginLg;
//margin-bottom: $interiorMargin;
padding: floor($formTBPad * .75) $formLRPad;
text-transform: uppercase;
}
@ -201,3 +200,102 @@ mct-representation:not(.s-status-editing) .l-inspect {
pointer-events: inherit;
}
}
// NEW COMPACT FORM, FOR USE IN INSPECTOR
// ul > li > label, control
// Make a new UL for each form section
// Allow control-first, controls-below
.l-inspect .tree ul li,
.inspector-config ul li {
padding: 2px 0;
}
.inspector-config {
$labelW: 40%;
$minW: $labelW;
ul {
margin-bottom: $interiorMarginLg;
li {
@include display(flex);
@include flex-wrap(wrap);
@include align-items(center);
label,
.control {
@include display(flex);
min-width: $minW;
}
label {
line-height: inherit;
padding: $interiorMarginSm 0;
width: $labelW;
}
.control {
@include flex-grow(1);
}
&:not(.section-header) {
&:not(.connects-to-previous) {
//border-top: 1px solid $colorFormLines;
}
}
&.connects-to-previous {
padding-top: 0 !important;
}
&.section-header {
margin-top: $interiorMarginLg;
border-top: 1px solid $colorFormLines;
}
&.controls-first {
.control {
@include flex-grow(0);
margin-right: $interiorMargin;
min-width: 0;
order: 1;
width: auto;
}
label {
@include flex-grow(1);
order: 2;
width: auto;
}
}
&.controls-under {
display: block;
.control, label {
display: block;
width: auto;
}
ul li {
border-top: none !important;
padding: 0;
}
}
}
}
.form-error {
// Block element that visually flags an error and contains a message
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
border-radius: $basicCr;
display: block;
padding: 1px 6px;
&:before {
content: $glyph-icon-alert-triangle;
display: inline;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
}
}
.tree .inspector-config {
margin-left: $treeVCW + $interiorMarginLg;
}

View File

@ -37,7 +37,7 @@
/********************************* CONTROLS */
@import "controls/breadcrumb";
@import "controls/buttons";
@import "controls/color-palette";
@import "controls/palette";
@import "controls/controls";
@import "controls/lists";
@import "controls/menus";
@ -70,6 +70,7 @@
@import "fixed-position";
@import "lists/tabular";
@import "plots/plots-main";
@import "plots/legend";
@import "iframe";
@import "views";
@import "items/item";
@ -80,3 +81,4 @@
@import "autoflow";
@import "features/imagery";
@import "features/time-display";
@import "widgets";

View File

@ -50,7 +50,6 @@
content:'';
font-family: symbolsfont;
font-size: 0.8em;
display: inline;
margin-right: $interiorMarginSm;
}
}

View File

@ -5,6 +5,7 @@
}
.l-view-section {
//@include test(orange, 0.1);
@include absPosDefault(0);
h2 {
color: #fff;

View File

@ -0,0 +1,301 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/************************************************************* WIDGET OBJECT */
.l-summary-widget {
// Widget layout classes here
@include ellipsize();
display: inline-block;
text-align: center;
.widget-label:before {
// Widget icon
font-size: 0.9em;
margin-right: $interiorMarginSm;
}
}
.s-summary-widget {
// Widget style classes here
@include boxShdw($shdwBtns);
border-radius: $basicCr;
border-style: solid;
border-width: 1px;
box-sizing: border-box;
cursor: default;
font-size: 0.8rem;
padding: $interiorMarginLg $interiorMarginLg * 2;
&[href] {
cursor: pointer;
}
}
.widget-edit-holder {
// Hide edit area when in browse mode
display: none;
}
.widget-rule-header {
@extend .l-flex-row;
@include align-items(center);
margin-bottom: $interiorMargin;
> .flex-elem {
&:not(:first-child) {
margin-left: $interiorMargin;
}
}
}
.widget-rules-wrapper,
.widget-rule-content,
.w-widget-test-data-content {
@include trans-prop-nice($props: (height, min-height, opacity), $dur: 250ms);
min-height: 0;
height: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
.widget-rules-wrapper {
flex: 1 1 auto !important;
}
.widget-rule-content.expanded {
overflow: visible !important;
min-height: 50px;
height: auto;
opacity: 1;
pointer-events: inherit;
}
.w-widget-test-data-content {
.l-enable {
padding: $interiorMargin 0;
}
.w-widget-test-data-items {
max-height: 20vh;
overflow-y: scroll !important;
padding-right: $interiorMargin;
}
}
.l-widget-thumb-wrapper,
.l-compact-form label {
$ruleLabelW: 40%;
$ruleLabelMaxW: 150px;
@include display(flex);
max-width: $ruleLabelMaxW;
width: $ruleLabelW;
}
.t-message-widget-no-data {
display: none;
}
/********************************************************** EDITING A WIDGET */
.s-status-editing > mct-view > .w-summary-widget {
// Classes for editor layout while editing a widget
// This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout
// being edited.
@include absPosDefault();
@extend .l-flex-col;
> .l-summary-widget {
// Main view of the summary widget
// Give some airspace and center the widget in the area
margin: 30px auto;
}
.widget-edit-holder {
display: flex; // Overrides `display: none` during Browse mode
.flex-accordion-holder {
// Needed because otherwise accordion elements "creep" when contents expand and contract
display: block !important;
}
&.expanded-widget-test-data {
.w-widget-test-data-content {
min-height: 50px;
height: auto;
opacity: 1;
pointer-events: inherit;
}
&:not(.expanded-widget-rules) {
// Test data is expanded and rules are collapsed
// Make text data take up all the vertical space
.flex-accordion-holder { display: flex; }
.widget-test-data {
flex-grow: 999999;
}
.w-widget-test-data-items {
max-height: inherit;
}
}
}
&.expanded-widget-rules {
.widget-rules-wrapper {
min-height: 50px;
height: auto;
opacity: 1;
pointer-events: inherit;
}
}
}
&.s-status-no-data {
.widget-edit-holder {
opacity: 0.3;
pointer-events: none;
}
.t-message-widget-no-data {
display: flex;
}
}
.l-compact-form {
// Overrides on .l-compact-form
ul {
&:last-child { margin: 0; }
li {
@include align-items(flex-start);
@include flex-wrap(nowrap);
line-height: 230%; // Provide enough space when controls wrap
padding: 2px 0;
&:not(.widget-rule-header) {
&:not(.connects-to-previous) {
border-top: 1px solid $colorFormLines;
}
}
&.connects-to-previous {
padding: $interiorMargin 0;
}
> label {
display: block; // Needed to align text to right
text-align: right;
}
}
}
&.s-widget-test-data-item {
// Single line of ul li label span, etc.
ul {
li {
border: none !important;
> label {
display: inline-block;
width: auto;
text-align: left;
}
}
}
}
}
}
.widget-edit-holder {
font-size: 0.8rem;
}
.widget-rules-wrapper {
// Wrapper area that holds n rules
box-sizing: border-box;
overflow-y: scroll;
padding-right: $interiorMargin;
}
.l-widget-rule,
.l-widget-test-data-item {
box-sizing: border-box;
margin-bottom: $interiorMarginSm;
padding: $interiorMargin $interiorMarginLg;
}
.l-widget-thumb-wrapper {
@extend .l-flex-row;
@include align-items(center);
> span { display: block; }
.grippy-holder,
.view-control {
margin-right: $interiorMargin;
width: 1em;
height: 1em;
}
.widget-thumb {
@include flex(1 1 auto);
width: 100%;
}
}
.rule-title {
@include flex(0 1 auto);
color: pullForward($colorBodyFg, 50%);
}
.rule-description {
@include flex(1 1 auto);
@include ellipsize();
color: pushBack($colorBodyFg, 20%);
}
.s-widget-rule,
.s-widget-test-data-item {
background-color: rgba($colorBodyFg, 0.1);
border-radius: $basicCr;
}
.widget-thumb {
@include ellipsize();
@extend .s-summary-widget;
@extend .l-summary-widget;
padding: $interiorMarginSm $interiorMargin;
}
// Hide and show elements in the rule-header on hover
.l-widget-rule,
.l-widget-test-data-item {
.grippy,
.l-rule-action-buttons-wrapper,
.l-condition-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 500ms);
opacity: 0;
}
&:hover {
.grippy,
.l-rule-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
.t-condition {
&:hover {
.l-condition-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
}
}

View File

@ -150,6 +150,26 @@
}
}
/******************************************************** VIEW CONTROLS */
// Expand/collapse > and v arrows, used in tree and plot legend
// Moved this over from a tree-only context 5/18/17
.view-control {
@extend .ui-symbol;
cursor: pointer;
height: 1em; width: 1em;
line-height: inherit;
&:before {
position: absolute;
@include trans-prop-nice(transform, 100ms);
content: $glyph-icon-arrow-right;
@include transform-origin(center);
}
&.expanded:before {
@include transform(rotate(90deg));
}
}
/******************************************************** CUSTOM CHECKBOXES */
label.checkbox.custom,
label.radio.custom {
@ -261,7 +281,7 @@ input[type="number"] {
input[type="text"].lg { width: 100% !important; }
.l-input-med input[type="text"],
input[type="text"].med { width: 200px !important; }
input[type="text"].sm { width: 50px !important; }
input[type="text"].sm, input[type="number"].sm { width: 50px !important; }
.l-numeric input[type="text"],
input[type="text"].numeric { text-align: right; }
@ -317,14 +337,10 @@ input[type="text"].s-input-inline,
.select {
@include btnSubtle($bg: $colorSelectBg);
@extend .icon-arrow-down; // Context arrow
@if $shdwBtns != none {
margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
}
display: inline-block;
padding: 0 $interiorMargin;
overflow: hidden;
position: relative;
line-height: $formInputH;
select {
@include appearance(none);
box-sizing: border-box;
@ -340,11 +356,13 @@ input[type="text"].s-input-inline,
}
}
&:before {
pointer-events: none;
@include transform(translateY(-50%));
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
display: block;
pointer-events: none;
position: absolute;
right: $interiorMargin; top: 0;
right: $interiorMargin;
top: 50%;
}
}
@ -396,8 +414,7 @@ input[type="text"].s-input-inline,
.l-elem-wrapper {
mct-representation {
// Holds the context-available item
// Must have min-width to make flex work properly
// in Safari
// Must have min-width to make flex work properly in Safari
min-width: 0.7em;
}
}
@ -563,7 +580,6 @@ input[type="text"].s-input-inline,
height: $h;
margin-top: 1 + floor($h/2) * -1;
@include btnSubtle(pullForward($colorBtnBg, 10%));
//border-radius: 50% !important;
}
@mixin sliderKnobRound() {
@ -578,7 +594,6 @@ input[type="text"].s-input-inline,
input[type="range"] {
// HTML5 range inputs
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
background: transparent; /* Otherwise white in Chrome */
&:focus {
@ -736,6 +751,30 @@ textarea {
}
}
.view-switcher,
.t-btn-view-large {
@include trans-prop-nice-fade($controlFadeMs);
}
.view-control {
@extend .icon-arrow-right;
cursor: pointer;
font-size: 0.75em;
&:before {
position: absolute;
@include trans-prop-nice(transform, 100ms);
@include transform-origin(center);
}
&.expanded:before {
@include transform(rotate(90deg));
}
}
.grippy {
@extend .icon-grippy;
cursor: move;
}
/******************************************************** BROWSER ELEMENTS */
body.desktop {
::-webkit-scrollbar {

View File

@ -29,23 +29,27 @@
}
.icon {
font-size: 16px; //120%;
font-size: 16px;
}
.title-label {
margin-left: $interiorMarginSm;
}
.icon-swatch,
.color-swatch {
// Used in color menu buttons in toolbar
$d: 10px;
display: inline-block;
border: 1px solid rgba($colorBtnFg, 0.2);
height: $d;
width: $d;
height: $d; width: $d;
line-height: $d;
vertical-align: middle;
margin-left: $interiorMarginSm;
margin-top: -2px;
&:not(.no-selection) {
border-color: transparent;
}
}
&:after {

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/******************************************************************* STATUS BLOCK ELEMS */
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
$bgPb: 30%;
$bgPbD: 10%;
@ -120,7 +120,11 @@
}
.status-indicator {
background: none !important;
margin-right: $interiorMarginSm;
&[class*='s-status']:before {
font-size: 1em;
}
}
.count {
@ -136,7 +140,7 @@
}
}
/* Styles for messages and message banners */
/******************************************************************* MESSAGE BANNERS */
.message {
&.block {
border-radius: $basicCr;
@ -192,7 +196,6 @@
padding: 0 $interiorMargin;
}
.close {
//@include test(red, 0.7);
cursor: pointer;
font-size: 7px;
width: 8px;
@ -236,132 +239,147 @@
}
}
@mixin messageBlock($iconW: 32px) {
.type-icon.message-type {
/******************************************************************* MESSAGES */
/* Contexts:
In .t-message-list
In .overlay as a singleton
Inline in the view area
*/
// Archetypal message
.l-message {
$iconW: 32px;
@include display(flex);
@include flex-direction(row);
@include align-items(stretch);
padding: $interiorMarginLg;
&:before {
// Icon
@include flex(0 1 auto);
@include txtShdw($shdwStatusIc);
@extend .icon-bell;
color: $colorStatusDefault;
font-size: $iconW;
padding: 1px;
width: $iconW + 2;
margin-right: $interiorMarginLg;
}
.message-severity-info .type-icon.message-type {
&.message-severity-info:before {
@extend .icon-info;
color: $colorInfo;
}
.message-severity-alert .type-icon.message-type {
@extend .icon-bell;
&.message-severity-alert:before {
color: $colorWarningLo;
}
.message-severity-error .type-icon.message-type {
&.message-severity-error:before {
@extend .icon-alert-rect;
color: $colorWarningHi;
}
}
/* Paths:
t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message >
message-type > (icon)
message-contents >
top-bar >
title
hint
editor >
(if displaying list of messages)
ul > li > l-message >
... same as above
bottom-bar
*/
.l-message {
.w-message-contents {
@include flex(1 1 auto);
@include display(flex);
@include flex-direction(row);
@include align-items(stretch);
.type-icon.message-type {
@include flex(0 1 auto);
position: relative;
}
.message-contents {
@include flex(1 1 auto);
margin-left: $overlayMargin;
position: relative;
@include flex-direction(column);
.top-bar,
> div,
> span {
//@include test(red);
margin-bottom: $interiorMargin;
}
.message-body {
@include flex(1 1 100%);
}
}
// Singleton in an overlay dialog
.t-message-single .l-message,
.t-message-single.l-message {
$iconW: 80px;
@include absPosDefault();
padding: 0;
&:before {
font-size: $iconW;
width: $iconW + 2;
}
.title {
font-size: 1.2em;
}
}
// Singleton inline in a view
.t-message-inline .l-message,
.t-message-inline.l-message {
border-radius: $controlCr;
&.message-severity-info { background-color: rgba($colorInfo, 0.3); }
&.message-severity-alert { background-color: rgba($colorWarningLo, 0.3); }
&.message-severity-error { background-color: rgba($colorWarningHi, 0.3); }
.w-message-contents.l-message-body-only {
.message-body {
margin-bottom: $interiorMarginLg * 2;
margin-top: $interiorMargin;
}
}
}
// In a list
.t-message-list {
@include absPosDefault();
@include display(flex);
@include flex-direction(column);
// Message as singleton
.t-message-single {
@include messageBlock(80px);
}
body.desktop .t-message-single {
.l-message,
.bottom-bar {
@include absPosDefault();
> div,
> span {
margin-bottom: $interiorMargin;
}
.bottom-bar {
top: auto;
height: $ovrFooterH;
.w-messages {
@include flex(1 1 100%);
overflow-y: auto;
padding-right: $interiorMargin;
}
// Each message
.l-message {
border-radius: $controlCr;
background: rgba($colorOvrFg, 0.1);
margin-bottom: $interiorMargin;
.hint,
.bottom-bar {
text-align: left;
}
}
}
@include phonePortrait {
.t-message-single {
.l-message {
@include flex-direction(column);
.message-contents { margin-left: 0; }
}
.type-icon.message-type {
.t-message-single .l-message,
.t-message-single.l-message {
@include flex-direction(column);
&:before {
margin-right: 0;
margin-bottom: $interiorMarginLg;
width: 100%;
text-align: center;
width: 100%;
}
.bottom-bar {
text-align: center !important;
}
}
}
// Messages in list
.t-message-list {
@include messageBlock(32px);
.message-contents {
.l-message {
border-radius: $controlCr;
background: rgba($colorOvrFg, 0.1);
margin-bottom: $interiorMargin;
padding: $interiorMarginLg;
.message-contents,
.bottom-bar {
position: relative;
}
.message-contents {
font-size: 0.9em;
margin-left: $interiorMarginLg;
.message-action { color: pushBack($colorOvrFg, 20%); }
.bottom-bar { text-align: left; }
}
.top-bar,
.message-body {
margin-bottom: $interiorMarginLg;
text-align: center;
.s-button {
display: block;
width: 100%;
}
}
}
}
body.desktop .t-message-list {
.message-contents .l-message { margin-right: $interiorMarginLg; }
.w-message-contents { padding-right: $interiorMargin; }
}
// Alert elements in views
@ -380,10 +398,6 @@ body.desktop .t-message-list {
.object-header {
.t-object-alert {
display: inline;
&.t-alert-unsynced {
@extend .icon-alert-triangle;
color: $colorPausedBg;
}
}
}
}

View File

@ -19,11 +19,10 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.l-color-palette {
.l-palette {
$d: 16px;
$colorsPerRow: 10;
$m: 1;
$colorSelectedColor: #fff;
box-sizing: border-box;
padding: $interiorMargin !important;
@ -33,46 +32,41 @@
line-height: $d;
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
&.l-option-row {
margin-bottom: $interiorMargin;
.s-palette-item {
border-color: $colorPaletteFg;
}
}
.l-palette-item {
box-sizing: border-box;
@include txtShdwSubtle(0.8);
@include trans-prop-nice-fade(0.25s);
border: 1px solid transparent;
color: $colorSelectedColor;
display: block;
float: left;
height: $d; width: $d;
line-height: $d * 0.9;
margin: 0 ($m * 1px) ($m * 1px) 0;
position: relative;
text-align: center;
&:before {
// Check mark for selected items
font-size: 0.8em;
}
}
.s-palette-item {
border: 1px solid transparent;
color: $colorPaletteFg;
text-shadow: $shdwPaletteFg;
@include trans-prop-nice-fade(0.25s);
&:hover {
@include trans-prop-nice-fade(0);
border-color: $colorSelectedColor !important;
border-color: $colorPaletteSelected !important;
}
&.selected {
border-color: $colorPaletteSelected;
box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
}
}
.l-palette-item-label {
margin-left: $interiorMargin;
}
&.l-option-row {
margin-bottom: $interiorMargin;
.s-palette-item {
border-color: $colorBodyFg;
}
}
}
}
}

View File

@ -20,7 +20,19 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.section-header {
border-radius: $basicCr;
background: $colorFormSectionHeader;
color: lighten($colorBodyFg, 20%);
font-size: inherit;
margin: $interiorMargin 0;
padding: $formTBPad $formLRPad;
text-transform: uppercase;
.view-control {
display: inline-block;
margin-right: $interiorMargin;
width: 1em;
height: 1em;
}
}
.form {
@ -41,15 +53,6 @@
}
}
.section-header {
border-radius: $basicCr;
background: $colorFormSectionHeader;
$c: lighten($colorBodyFg, 20%);
color: $c;
font-size: 0.8em;
padding: $formTBPad $formLRPad;
}
.form-row {
$m: $interiorMargin;
box-sizing: border-box;
@ -57,9 +60,6 @@
margin-bottom: $interiorMarginLg * 2;
padding: $formTBPad 0;
position: relative;
//&ng-form {
// display: block;
//}
&.first {
border-top: none;
@ -171,3 +171,106 @@
padding: $interiorMargin;
}
}
/**************************************************************************** COMPACT FORM */
// ul > li > label, control
// Make a new UL for each form section
// Allow control-first, controls-below
// TO-DO: migrate work in branch ch-plot-styling that users .inspector-config to use classes below instead
.l-compact-form .tree ul li,
.l-compact-form ul li {
padding: 2px 0;
}
.l-compact-form {
$labelW: 40%;
$minW: $labelW;
ul {
margin-bottom: $interiorMarginLg;
li {
@include display(flex);
@include flex-wrap(wrap);
@include align-items(center);
label,
.control {
@include display(flex);
}
label {
line-height: inherit;
width: $labelW;
}
.controls {
@include flex-grow(1);
margin-left: $interiorMargin;
input[type="text"],
input[type="search"],
input[type="number"],
.select {
height: $btnStdH;
line-height: $btnStdH;
vertical-align: middle;
}
.e-control {
// Individual form controls
&:not(:first-child) {
margin-left: $interiorMarginSm;
}
}
}
&.connects-to-previous {
padding-top: 0;
}
&.section-header {
margin-top: $interiorMarginLg;
border-top: 1px solid $colorFormLines;
}
&.controls-first {
.control {
@include flex-grow(0);
margin-right: $interiorMargin;
min-width: 0;
order: 1;
width: auto;
}
label {
@include flex-grow(1);
order: 2;
width: auto;
}
}
&.controls-under {
display: block;
.control, label {
display: block;
width: auto;
}
ul li {
border-top: none !important;
padding: 0;
}
}
}
}
.form-error {
// Block element that visually flags an error and contains a message
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
border-radius: $basicCr;
display: block;
padding: 1px 6px;
&:before {
content: $glyph-icon-alert-triangle;
display: inline;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
}
}

View File

@ -34,18 +34,7 @@ body.touch {
line-height: $mobileTreeItemH !important;
.view-control {
font-size: 1em;
margin-right: $interiorMargin;
width: ceil($mobileTreeItemH * 0.75);
&.has-children {
&:before {
content: $glyph-icon-arrow-down;
left: 50%;
@include transform(translateX(-50%) rotate(-90deg));
}
&.expanded:before {
@include transform(translateX(-50%) rotate(0deg));
}
}
width: ceil($mobileTreeItemH * 0.5);
}
.t-object-label {
line-height: inherit;

View File

@ -79,6 +79,7 @@
// Dialog boxes, size constrained and centered in desktop/tablet
&.l-dialog {
font-size: 0.8rem;
.s-button {
&:not(.major) {
@include btnSubtle($bg: $colorOvrBtnBg, $bgHov: pullForward($colorOvrBtnBg, 10%), $fg: $colorOvrBtnFg, $fgHov: $colorOvrBtnFg, $ic: $colorOvrBtnFg, $icHov: $colorOvrBtnFg);
@ -125,9 +126,9 @@
@include containerSubtle($colorOvrBg, $colorOvrFg);
}
.title {
.dialog-title {
@include ellipsize();
font-size: 1.2em;
font-size: 1.5em;
line-height: 120%;
margin-bottom: $interiorMargin;
}

View File

@ -0,0 +1,208 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
.gl-plot {
.gl-plot-legend {
min-height: $plotLegendH;
.view-control {
font-size: 1em;
margin-right: $interiorMarginSm;
}
table {
table-layout: fixed;
tr {
display: table-row;
}
th,
td {
@include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
display: table-cell;
padding: 1px 3px; // Tighter than standard tabular padding
}
}
&.hover-on-plot {
// User is hovering over the plot to get a value at a point
.hover-value-enabled {
background-color: $legendHoverValueBg;
border-radius: $smallCr;
padding: 0 $interiorMarginSm;
&:before {
opacity: 0.5;
}
&.cursor-hover,
.value-to-display-nearestTimestamp,
.value-to-display-nearestValue
{
@extend .icon-crosshair-12px;
&:before {
font-size: 9px;
}
}
&.value-to-display-min:before {
content: 'MIN ';
}
&.value-to-display-max:before {
content: 'MAX ';
}
}
}
}
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item {
// General styles for legend items, both expanded and collapsed legend states
.plot-series-color-swatch {
border-radius: $smallCr;
border: 1px solid $colorBodyBg;
display: inline-block;
height: $plotSwatchD;
width: $plotSwatchD;
}
.plot-series-name {
display: inline;
}
.plot-series-value {
@include ellipsize();
}
}
/***************** GENERAL STYLES, COLLAPSED */
&.plot-legend-collapsed {
// .plot-legend-item is a span of spans.
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
&.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
.plot-legend-item {
display: flex;
align-items: center;
&:not(:first-child) {
margin-left: $interiorMarginLg;
}
.plot-series-swatch-and-name,
.plot-series-value {
@include ellipsize();
flex: 1 1 auto;
}
.plot-series-swatch-and-name {
margin-right: $interiorMarginSm;
}
.plot-series-value {
text-align: left;
width: 170px;
}
}
}
/***************** GENERAL STYLES, EXPANDED */
&.plot-legend-expanded {
.gl-plot-legend {
max-height: 70%;
}
.plot-wrapper-expanded-legend {
overflow-y: auto;
}
&.plot-legend-top .gl-plot-legend {
margin-bottom: $interiorMargin;
}
&.plot-legend-bottom .gl-plot-legend {
margin-top: $interiorMargin;
}
}
/***************** TOP OR BOTTOM */
&.plot-legend-top,
&.plot-legend-bottom {
// General styles when legend is on the top or bottom
@extend .l-flex-col;
&.plot-legend-collapsed {
// COLLAPSED ON TOP OR BOTTOM
.plot-wrapper-collapsed-legend {
display: flex;
flex: 1 1 auto;
overflow: hidden;
}
}
}
/***************** EITHER SIDE */
&.plot-legend-left,
&.plot-legend-right {
@extend .l-flex-row;
// If the legend is expanded, use flex-col instead so that the legend gets the width it needs.
&.plot-legend-expanded {
// EXPANDED, ON EITHER SIDE
@extend .l-flex-col;
}
&.plot-legend-collapsed {
// COLLAPSED, ON EITHER SIDE
.gl-plot-legend {
max-height: inherit;
width: 25%;
}
.plot-wrapper-collapsed-legend {
display: flex;
flex-flow: column nowrap;
min-width: 0;
flex: 1 1 auto;
overflow-y: auto;
}
.plot-legend-item {
margin-bottom: 1px;
margin-left: 0;
flex-wrap: wrap;
.plot-series-swatch-and-name {
flex: 0 1 auto;
min-width: 20%;
}
.plot-series-value {
flex: 0 1 auto;
width: auto;
}
}
}
}
/***************** ON BOTTOM OR RIGHT */
&.plot-legend-right:not(.plot-legend-expanded),
&.plot-legend-bottom {
.gl-plot-legend {
order: 2;
}
.plot-wrapper-axis-and-display-area {
order: 1;
}
}
}

View File

@ -20,10 +20,42 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.abs.holder-plot {
// Fend off the scrollbar when less than min-height;
right: $interiorMargin;
right: $interiorMargin; // Fend off the scrollbar when less than min-height;
.t-object-alert.t-alert-unsynced {
display: none;
}
}
/********************************************* STACKED PLOT LAYOUT */
.t-plot-stacked {
.l-view-section {
// Make this a flex container
display: flex;
flex-flow: column nowrap;
.gl-plot.child-frame {
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
}
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: block;
}
}
}
.gl-plot {
color: $colorPlotFg;
font-size: 0.7rem;
@ -32,6 +64,19 @@
height: 100%;
min-height: $plotMinH;
/********************************************* AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area {
margin-top: $interiorMargin; // Keep the top tick label from getting clipped
position: relative;
flex: 1 1 auto;
.t-object-alert {
position: absolute;
display: block;
font-size: 1.5em;
top: $interiorMarginSm; left: $interiorMarginSm;
}
}
.gl-plot-wrapper-display-area-and-x-axis {
// Holds the plot area and the X-axis only
position: absolute;
@ -49,7 +94,6 @@
}
.gl-plot-axis-area.gl-plot-x {
//@include test(green);
top: auto;
right: 0;
bottom: 0;
@ -63,7 +107,7 @@
.gl-plot-axis-area {
position: absolute;
&.gl-plot-y {
top: $plotLegendH + $interiorMargin;
top: nth($plotDisplayArea, 1);
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
@ -158,17 +202,6 @@
}
}
.gl-plot-legend {
position: absolute;
top: 0;
right: 0;
bottom: auto;
left: 0;
height: $plotLegendH;
overflow-x: hidden;
overflow-y: auto;
}
/****************************** Limits and Out-of-Bounds data */
.l-limit-bar,
@ -235,39 +268,6 @@
border: 1px solid $colorPlotAreaBorder;
}
.gl-plot-legend,
.legend {
.plot-legend-item,
.legend-item {
display: inline-block;
margin-right: $interiorMarginLg;
margin-bottom: $interiorMarginSm;
span {
vertical-align: middle;
}
.plot-color-swatch,
.color-swatch {
border-radius: 2px;
display: inline-block;
height: $plotSwatchD;
width: $plotSwatchD;
}
}
}
.gl-plot-legend {
.plot-legend-item {
border-radius: $smallCr;
line-height: 1.5em;
padding: 0px $itemPadLR;
.plot-color-swatch {
border: 1px solid $colorBodyBg;
height: $plotSwatchD + 1;
width: $plotSwatchD + 1;
}
}
}
.tick {
position: absolute;
border: 0 $colorPlotHash solid;
@ -277,10 +277,14 @@
}
}
.s-tick-label {
font-size: 0.7rem;
}
.gl-plot-tick,
.tick-label {
@extend .s-tick-label;
@include reverseEllipsis();
font-size: 0.7rem;
position: absolute;
&.gl-plot-x-tick-label,
&.tick-label-x {
@ -320,3 +324,76 @@
right: 0; left: 0;
}
}
/********************************************* HEATMAPS */
.l-heatmap {
right: $interiorMargin; // Fend off from the scrollbar
}
.h-heatmap-legend {
flex: 1 1 1%; // Pertains to the width of legend area
height: 50%;
min-height: 100px !important;
canvas.heatmap-legend {
width: 10px;
}
.h-heatmap-legend-ticks {
justify-content: space-between;
margin-left: $interiorMarginSm;
}
}
.h-heatmap-grid-outer {
$ticksYW: 40px;
flex: 1 1 100% !important;
margin-left: $ticksYW + $interiorMarginLg;
.h-heatmap-grid-and-ticks {
position: relative;
height: 0;
width: 100%;
padding-bottom: 100%;
.heatmap-grid {
width: 100%;
height:100%;
position: absolute;
}
.s-tick-label {
flex: 1;
min-width: 0;
overflow: hidden;
}
.l-heatmap-ticks-x,
.l-heatmap-ticks-y {
justify-content: stretch;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: 2;
}
.l-heatmap-ticks-x {
@extend .l-flex-row;
bottom: auto;
top: 100%;
transform: translateY($interiorMargin);
.s-tick-label {
text-align: center;
}
}
.l-heatmap-ticks-y {
@extend .l-flex-col;
left: auto;
right: 100%;
text-align: right;
transform: translateX($interiorMargin * -1);
.s-tick-label {
display: flex;
align-items: center;
text-align: right;
}
}
}
}

View File

@ -52,21 +52,13 @@ ul.tree {
.view-control {
color: $colorItemTreeVC;
font-size: 0.75em;
margin-right: $interiorMargin;
height: 100%;
line-height: inherit;
width: $treeVCW;
&.has-children {
&:before {
position: absolute;
@include trans-prop-nice(transform, 100ms);
content: "\e904";
@include transform-origin(center);
}
&.expanded:before {
@include transform(rotate(90deg));
}
&:before { display: block; }
&.no-children {
&:before { display: none; }
}
}

View File

@ -44,7 +44,10 @@
&.t-object-type-timer,
&.t-object-type-clock,
&.t-object-type-hyperlink {
&.t-object-type-hyperlink,
&.t-object-type-summary-widget,
&.no-frame .t-object-type-fixed-display,
&.no-frame .t-object-type-layout {
// Hide the right side buttons for objects where they don't make sense
// Note that this will hide the view Switcher button if applied
// to an object that has it.
@ -103,7 +106,7 @@
}
&.t-frame-outer > .t-rep-frame {
&.contents {
$m: 2px;
$m: 0px;
top: $m;
right: $m;
bottom: $m;
@ -125,14 +128,21 @@
pointer-events: none !important;
}
/********************************************************** OBJECT TYPES */
.t-object-type-hyperlink {
/********************************************************** OBJECT TYPES */
.t-object-type-hyperlink,
.t-object-type-summary-widget {
.object-holder {
overflow: hidden;
}
.w-summary-widget,
.l-summary-widget,
.l-hyperlink.s-button {
// When a hyperlink is a button in a frame, make it expand to fill out to the object-holder
// Some object types expand to the full size of the object-holder.
@extend .abs;
}
.l-summary-widget,
.l-hyperlink.s-button {
.label {
@include ellipsize();
@include transform(translateY(-50%));

View File

@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.s-hover-border {
border: 1px dotted transparent;
border: none;
}
.s-status-editing {

View File

@ -83,9 +83,9 @@ define([
this.activeObject = domainObject;
if (domainObject && domainObject.hasCapability('composition')) {
$(this.toggleView.elements()).addClass('has-children');
$(this.toggleView.elements()).removeClass('no-children');
} else {
$(this.toggleView.elements()).removeClass('has-children');
$(this.toggleView.elements()).addClass('no-children');
}
if (domainObject && domainObject.hasCapability('status')) {

View File

@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
$stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
$legendCollapsedNameMaxW: 50%;
$legendHoverValueBg: rgba($colorBodyFg, 0.1);
// Tree
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
@ -243,6 +245,12 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
$colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
// Palettes
$colorPaletteFg: pullForward($colorMenuBg, 30%);
$colorPaletteSelected: #fff;
$shdwPaletteFg: black 0 0 2px;
$shdwPaletteSelected: inset 0 0 0 1px #000;
// About Screen
$colorAboutLink: #84b3ff;

View File

@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
$stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
$legendCollapsedNameMaxW: 50%;
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
// Tree
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
@ -243,6 +245,12 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
// Palettes
$colorPaletteFg: pullForward($colorMenuBg, 30%);
$colorPaletteSelected: #333;
$shdwPaletteFg: none;
$shdwPaletteSelected: inset 0 0 0 1px #fff;
// About Screen
$colorAboutLink: #84b3ff;

View File

@ -23,10 +23,13 @@
define([
"moment-timezone",
"./src/indicators/ClockIndicator",
"./src/indicators/FollowIndicator",
"./src/services/TickerService",
"./src/services/TimerService",
"./src/controllers/ClockController",
"./src/controllers/TimerController",
"./src/controllers/RefreshingController",
"./src/actions/FollowTimerAction",
"./src/actions/StartTimerAction",
"./src/actions/RestartTimerAction",
"./src/actions/StopTimerAction",
@ -37,10 +40,13 @@ define([
], function (
MomentTimezone,
ClockIndicator,
FollowIndicator,
TickerService,
TimerService,
ClockController,
TimerController,
RefreshingController,
FollowTimerAction,
StartTimerAction,
RestartTimerAction,
StopTimerAction,
@ -80,6 +86,11 @@ define([
"CLOCK_INDICATOR_FORMAT"
],
"priority": "preferred"
},
{
"implementation": FollowIndicator,
"depends": ["timerService"],
"priority": "fallback"
}
],
"services": [
@ -90,6 +101,11 @@ define([
"$timeout",
"now"
]
},
{
"key": "timerService",
"implementation": TimerService,
"depends": ["openmct"]
}
],
"controllers": [
@ -134,6 +150,15 @@ define([
}
],
"actions": [
{
"key": "timer.follow",
"implementation": FollowTimerAction,
"depends": ["timerService"],
"category": "contextual",
"name": "Follow Timer",
"cssClass": "icon-clock",
"priority": "optional"
},
{
"key": "timer.start",
"implementation": StartTimerAction,

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -21,27 +21,34 @@
*****************************************************************************/
define(
['../src/PlotOptionsForm'],
function (PlotOptionsForm) {
[],
function () {
describe("The Plot Options form", function () {
var plotOptionsForm;
/**
* Designates a specific timer for following. Timelines, for example,
* use the actively followed timer to display a time-of-interest line
* and interpret time conductor bounds in the Timeline's relative
* time frame.
*
* @implements {Action}
* @memberof platform/features/clock
* @constructor
* @param {ActionContext} context the context for this action
*/
function FollowTimerAction(timerService, context) {
var domainObject = context.domainObject;
this.perform =
timerService.setTimer.bind(timerService, domainObject);
}
beforeEach(function () {
FollowTimerAction.appliesTo = function (context) {
var model =
(context.domainObject && context.domainObject.getModel()) ||
{};
plotOptionsForm = new PlotOptionsForm();
});
return model.type === 'timer';
};
it("defines form specs for x-axis, y-axis, and series data", function () {
expect(plotOptionsForm.xAxisForm).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections[0].rows).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections[0].rows.length).toBeGreaterThan(0);
expect(plotOptionsForm.yAxisForm).toBeDefined();
expect(plotOptionsForm.plotSeriesForm).toBeDefined();
});
});
return FollowTimerAction;
}
);

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -20,26 +20,38 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
function Region(element) {
this.activeView = undefined;
this.element = element;
define(
['moment'],
function (moment) {
var NO_TIMER = "No timer being followed";
/**
* Indicator that displays the active timer, as well as its
* current state.
* @implements {Indicator}
* @memberof platform/features/clock
*/
function FollowIndicator(timerService) {
this.timerService = timerService;
}
FollowIndicator.prototype.getGlyphClass = function () {
return "";
};
FollowIndicator.prototype.getCssClass = function () {
return (this.timerService.getTimer()) ? "icon-timer s-status-ok" : "icon-timer";
};
FollowIndicator.prototype.getText = function () {
var timer = this.timerService.getTimer();
return (timer) ? 'Following timer ' + timer.getModel().name : NO_TIMER;
};
FollowIndicator.prototype.getDescription = function () {
return "";
};
return FollowIndicator;
}
Region.prototype.clear = function () {
if (this.activeView) {
this.activeView.destroy();
this.activeView = undefined;
}
};
Region.prototype.show = function (view) {
this.clear();
this.activeView = view;
if (this.activeView) {
this.activeView.show(this.element);
}
};
return Region;
});
);

View File

@ -0,0 +1,102 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(['EventEmitter'], function (EventEmitter) {
/**
* Tracks the currently-followed Timer object. Used by
* timelines et al to synchronize to a particular timer.
*
* The TimerService emits `change` events when the active timer
* is changed.
*/
function TimerService(openmct) {
EventEmitter.apply(this);
this.time = openmct.time;
}
TimerService.prototype = Object.create(EventEmitter.prototype);
/**
* Set (or clear, if `timer` is undefined) the currently active timer.
* @param {DomainObject} timer the new active timer
* @emits change
*/
TimerService.prototype.setTimer = function (timer) {
this.timer = timer;
this.emit('change');
};
/**
* Get the currently active timer.
* @return {DomainObject} the active timer
* @emits change
*/
TimerService.prototype.getTimer = function () {
return this.timer;
};
/**
* Check if there is a currently active timer.
* @return {boolean} true if there is a timer
*/
TimerService.prototype.hasTimer = function () {
return !!this.timer;
};
/**
* Convert the provided timestamp to milliseconds relative to
* the active timer.
* @return {number} milliseconds since timer start
*/
TimerService.prototype.convert = function (timestamp) {
var clock = this.time.clock();
var canConvert = this.hasTimer() &&
!!clock &&
this.timer.getModel().timerState !== 'stopped';
if (!canConvert) {
return undefined;
}
var now = clock.currentValue();
var model = this.timer.getModel();
var delta = model.timerState === 'paused' ? now - model.pausedTime : 0;
var epoch = model.timestamp;
return timestamp - epoch - delta;
};
/**
* Get the value of the active clock, adjusted to be relative to the active
* timer. If there is no clock or no active timer, this will return
* `undefined`.
* @return {number} milliseconds since the start of the active timer
*/
TimerService.prototype.now = function () {
var clock = this.time.clock();
return clock && this.convert(clock.currentValue());
};
return TimerService;
});

View File

@ -0,0 +1,80 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define([
"../../src/actions/FollowTimerAction"
], function (FollowTimerAction) {
var TIMER_SERVICE_METHODS =
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
describe("The Follow Timer action", function () {
var testContext;
var testModel;
beforeEach(function () {
testModel = {};
testContext = { domainObject: { getModel: function () {
return testModel;
} } };
});
it("is applicable to timers", function () {
testModel.type = "timer";
expect(FollowTimerAction.appliesTo(testContext)).toBe(true);
});
it("is inapplicable to non-timers", function () {
testModel.type = "folder";
expect(FollowTimerAction.appliesTo(testContext)).toBe(false);
});
describe("when instantiated", function () {
var mockTimerService;
var action;
beforeEach(function () {
mockTimerService = jasmine.createSpyObj(
'timerService',
TIMER_SERVICE_METHODS
);
action = new FollowTimerAction(mockTimerService, testContext);
});
it("does not interact with the timer service", function () {
TIMER_SERVICE_METHODS.forEach(function (method) {
expect(mockTimerService[method]).not.toHaveBeenCalled();
});
});
describe("and performed", function () {
beforeEach(function () {
action.perform();
});
it("sets the active timer", function () {
expect(mockTimerService.setTimer)
.toHaveBeenCalledWith(testContext.domainObject);
});
});
});
});
});

View File

@ -0,0 +1,61 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(["../../src/indicators/FollowIndicator"], function (FollowIndicator) {
var TIMER_SERVICE_METHODS =
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
describe("The timer-following indicator", function () {
var mockTimerService;
var indicator;
beforeEach(function () {
mockTimerService =
jasmine.createSpyObj('timerService', TIMER_SERVICE_METHODS);
indicator = new FollowIndicator(mockTimerService);
});
it("implements the Indicator interface", function () {
expect(indicator.getGlyphClass()).toEqual(jasmine.any(String));
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
expect(indicator.getText()).toEqual(jasmine.any(String));
expect(indicator.getDescription()).toEqual(jasmine.any(String));
});
describe("when a timer is set", function () {
var testModel;
var mockDomainObject;
beforeEach(function () {
testModel = { name: "some timer!" };
mockDomainObject = jasmine.createSpyObj('timer', ['getModel']);
mockDomainObject.getModel.andReturn(testModel);
mockTimerService.getTimer.andReturn(mockDomainObject);
});
it("displays the timer's name", function () {
expect(indicator.getText().indexOf(testModel.name))
.not.toEqual(-1);
});
});
});
});

View File

@ -0,0 +1,63 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define([
'../../src/services/TimerService'
], function (TimerService) {
describe("TimerService", function () {
var callback;
var mockmct;
var timerService;
beforeEach(function () {
callback = jasmine.createSpy('callback');
mockmct = { time: { clock: jasmine.createSpy('clock') } };
timerService = new TimerService(mockmct);
timerService.on('change', callback);
});
it("initially emits no change events", function () {
expect(callback).not.toHaveBeenCalled();
});
it("reports no current timer", function () {
expect(timerService.getTimer()).toBeUndefined();
});
describe("setTimer", function () {
var testTimer;
beforeEach(function () {
testTimer = { name: "I am some timer; you are nobody." };
timerService.setTimer(testTimer);
});
it("emits a change event", function () {
expect(callback).toHaveBeenCalled();
});
it("reports the current timer", function () {
expect(timerService.getTimer()).toBe(testTimer);
});
});
});
});

View File

@ -334,46 +334,6 @@ define([
"conversion": "number[]"
}
]
},
{
"key": "telemetry.panel",
"name": "Telemetry Panel",
"cssClass": "icon-telemetry-panel",
"description": "A panel for collecting telemetry elements.",
"priority": 899,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"properties": [
{
"name": "Layout Grid",
"control": "composite",
"items": [
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",
"property": "layoutGrid",
"conversion": "number[]"
}
]
}
]
}

View File

@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="frame frame-template t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation

View File

@ -1,37 +0,0 @@
# Plot README
## Chart
The `mct-chart` directive is used to support drawing of simple charts. It is
present to support the Plot view, and its functionality is limited to the
functionality that is relevant for that view.
This directive is used at the element level and takes one attribute, `draw`
which is an Angular expression which will should evaluate to a drawing object.
This drawing object should contain the following properties:
* `dimensions`: The size, in logical coordinates, of the chart area. A
two-element array or numbers.
* `origin`: The position, in logical coordinates, of the lower-left corner of
the chart area. A two-element array or numbers.
* `lines`: An array of lines (e.g. as a plot line) to draw, where each line is
expressed as an object containing:
* `buffer`: A Float32Array containing points in the line, in logical
coordinates, in sequential x,y pairs.
* `color`: The color of the line, as a four-element RGBA array, where
each element is a number in the range of 0.0-1.0.
* `points`: The number of points in the line.
* `boxes`: An array of rectangles to draw in the chart area. Each is an object
containing:
* `start`: The first corner of the rectangle, as a two-element array of
numbers, in logical coordinates.
* `end`: The opposite corner of the rectangle, as a two-element array of
numbers, in logical coordinates. color : The color of the line, as a
four-element RGBA array, where each element is a number in the range of
0.0-1.0.
While `mct-chart` is intended to support plots specifically, it does perform
some useful management of canvas objects (e.g. choosing between WebGL and Canvas
2D APIs for drawing based on browser support) so its usage is recommended when
its supported drawing primitives are sufficient for other charting tasks.

View File

@ -1,157 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define([
"./src/MCTChart",
"./src/PlotController",
"./src/policies/PlotViewPolicy",
"./src/PlotOptionsController",
"./src/services/ExportImageService",
"text!./res/templates/plot.html",
"text!./res/templates/plot-options-browse.html",
'legacyRegistry'
], function (
MCTChart,
PlotController,
PlotViewPolicy,
PlotOptionsController,
exportImageService,
plotTemplate,
plotOptionsBrowseTemplate,
legacyRegistry
) {
legacyRegistry.register("platform/features/plot", {
"name": "Plot view for telemetry",
"extensions": {
"views": [
{
"name": "Plot",
"key": "plot",
"cssClass": "icon-sine",
"template": plotTemplate,
"needs": [
"telemetry"
],
"priority": "preferred",
"delegation": true
}
],
"directives": [
{
"key": "mctChart",
"implementation": MCTChart,
"depends": [
"$interval",
"$log"
]
}
],
"controllers": [
{
"key": "PlotController",
"implementation": PlotController,
"depends": [
"$scope",
"$element",
"exportImageService",
"telemetryFormatter",
"telemetryHandler",
"throttle",
"PLOT_FIXED_DURATION",
"openmct"
]
},
{
"key": "PlotOptionsController",
"implementation": PlotOptionsController,
"depends": [
"$scope"
]
}
],
"services": [
{
"key": "exportImageService",
"implementation": exportImageService,
"depends": [
"$q",
"$timeout",
"$log",
"EXPORT_IMAGE_TIMEOUT"
]
}
],
"constants": [
{
"key": "PLOT_FIXED_DURATION",
"value": 900000,
"priority": "fallback",
"comment": "Fifteen minutes."
},
{
"key": "EXPORT_IMAGE_TIMEOUT",
"value": 500,
"priority": "fallback"
}
],
"policies": [
{
"category": "view",
"implementation": PlotViewPolicy,
"depends": [
"openmct"
]
}
],
"representations": [
{
"key": "plot-options-browse",
"template": plotOptionsBrowseTemplate
}
],
"licenses": [
{
"name": "FileSaver.js",
"version": "0.0.2",
"author": "Eli Grey",
"description": "File download initiator (for file exports)",
"website": "https://github.com/eligrey/FileSaver.js/",
"copyright": "Copyright © 2015 Eli Grey.",
"license": "license-mit",
"link": "https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md"
},
{
"name": "html2canvas",
"version": "0.4.1",
"author": "Niklas von Hertzen",
"description": "JavaScript HTML renderer",
"website": "https://github.com/niklasvh/html2canvas",
"copyright": "Copyright © 2012 Niklas von Hertzen.",
"license": "license-mit",
"link": "https://github.com/niklasvh/html2canvas/blob/master/LICENSE"
}
]
}
});
});

View File

@ -1,70 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT 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 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.
-->
<div ng-controller="PlotOptionsController" class="flex-elem grows l-inspector-part">
<em class="t-inspector-part-header" title="Display properties for this object">Plot Options</em>
<mct-form
ng-model="configuration.plot.xAxis"
structure="xAxisForm"
name="xAxisFormState"
class="flex-elem l-flex-row no-margin">
</mct-form>
<mct-form
ng-model="configuration.plot.yAxis"
structure="yAxisForm"
name="yAxisFormState"
class="flex-elem l-flex-row no-margin">
</mct-form>
<div class="form">
<div class="section-header ng-binding ng-scope">
Plot Series
</div>
<ul class="first flex-elem grows vscroll">
<ul class="tree">
<li ng-repeat="child in children">
<span ng-controller="ToggleController as toggle">
<span ng-controller="TreeNodeController as treeNode">
<span class="tree-item menus-to-left">
<span
class='ui-symbol view-control flex-elem has-children'
ng-class="{ expanded: toggle.isActive() }"
ng-click="toggle.toggle(); treeNode.trackExpansion()">
</span>
<mct-representation
class="rep-object-label"
key="'label'"
mct-object="child">
</mct-representation>
</span>
</span>
<mct-form
ng-class="{hidden: !toggle.isActive()}"
ng-model="configuration.plot.series[$index]"
structure="plotSeriesForm"
name="plotOptionsState"
class="flex-elem l-flex-row">
</mct-form>
</span>
</li>
</ul>
</ul>
</div>
</div>

View File

@ -1,165 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT 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 includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<span ng-controller="PlotController as plot"
class="abs holder holder-plot has-control-bar">
<div class="l-control-bar" ng-show="!plot.hideExportButtons">
<span class="l-btn-set">
<a class="s-button t-export labeled icon-download"
ng-click="plot.exportPNG()"
title="Export This View's Data as PNG">
PNG
</a>
<a class="s-button t-export labeled"
ng-click="plot.exportJPG()"
title="Export This View's Data as JPG">
JPG
</a>
</span>
</div>
<div class="l-view-section">
<div class="gl-plot"
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
ng-repeat="subplot in plot.getSubPlots()">
<div class="gl-plot-legend">
<span class='plot-legend-item'
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
ng-class="plot.getLegendClass(telemetryObject)">
<span class='plot-color-swatch'
ng-style="{ 'background-color': plot.getColor($index) }">
</span>
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
</span>
</div>
<div class="gl-plot-axis-area gl-plot-y">
<div class="gl-plot-label gl-plot-y-label">
{{axes[1].active.name}}
</div>
<div ng-repeat="tick in subplot.getRangeTicks()"
class="gl-plot-tick gl-plot-y-tick-label"
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%' }">
{{tick.label | reverse}}
</div>
<div class="gl-plot-y-options gl-plot-local-controls"
ng-if="axes[1].options.length > 1">
<div class='form-control shell select'>
<select class="form-control input shell"
ng-model="axes[1].active"
ng-options="option.name for option in axes[1].options">
</select>
</div>
</div>
</div>
<div class="gl-plot-wrapper-display-area-and-x-axis">
<mct-include key="'time-of-interest'"
class="l-toi-holder show-val"
ng-if="toiPerc"
ng-class="{ 'pinned': toiPinned, 'val-to-left': toiPerc > 80 }"
ng-style="{'left': toiPerc + '%'}"></mct-include>
<div class="gl-plot-coords"
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
{{subplot.getHoverCoordinates()}}
</div>
<div class="gl-plot-display-area"
ng-mouseenter="subplot.isHovering(true);"
ng-mouseleave="subplot.isHovering(false)"
ng-class="{ loading: plot.isRequestPending() }">
<!-- Out-of-bounds data indicators -->
<!-- ng-show is temporarily hard-coded in next element -->
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
<div class="gl-plot-hash hash-v"
ng-repeat="tick in subplot.getDomainTicks()"
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
</div>
<div class="gl-plot-hash hash-h"
ng-repeat="tick in subplot.getRangeTicks()"
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
</div>
<mct-chart draw="subplot.getDrawingObject()"
ng-if="subplot.getTelemetryObjects().length > 0"
ng-mousemove="subplot.hover($event)"
mct-drag="subplot.continueDrag($event)"
mct-drag-down="subplot.startDrag($event)"
mct-drag-up="subplot.endDrag($event); plot.update()">
</mct-chart>
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
ng-if="$first">
<a class="s-button icon-arrow-left"
ng-click="plot.stepBackPanZoom()"
ng-show="plot.isZoomed()"
title="Restore previous pan/zoom">
</a>
<a class="s-button icon-arrows-out"
ng-click="plot.unzoom()"
ng-show="plot.isZoomed()"
title="Reset pan/zoom">
</a>
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssClass}}"
ng-if="plot.getModeOptions().length > 1"
ng-controller="ClickAwayController as toggle">
<span class="l-click-area" ng-click="toggle.toggle()"></span>
<span>{{plot.getMode().name}}</span>
<div class="menu" ng-show="toggle.isActive()">
<ul>
<li ng-repeat="option in plot.getModeOptions()"
ng-click="plot.setMode(option); toggle.setState(false)"
class="{{option.cssClass}}">
{{option.name}}
</li>
</ul>
</div>
</div>
</div>
</div>
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
<div ng-repeat="tick in subplot.getDomainTicks()"
class="gl-plot-tick gl-plot-x-tick-label"
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
{{tick.label | reverse}}
</div>
<div class="gl-plot-label gl-plot-x-label">
{{axes[0].active.name}}
</div>
<div class="gl-plot-x-options gl-plot-local-controls"
ng-if="axes[0].options.length > 1">
<div class='form-control shell select'>
<select class="form-control input shell"
ng-model="axes[0].active"
ng-options="option.name for option in axes[0].options">
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</span>

View File

@ -1,117 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* Create a new chart which uses Canvas's 2D API for rendering.
*
* @memberof platform/features/plot
* @constructor
* @implements {platform/features/plot.Chart}
* @param {CanvasElement} canvas the canvas object to render upon
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable.
*/
function Canvas2DChart(canvas) {
this.canvas = canvas;
this.c2d = canvas.getContext('2d');
this.width = canvas.width;
this.height = canvas.height;
this.dimensions = [this.width, this.height];
this.origin = [0, 0];
if (!this.c2d) {
throw new Error("Canvas 2d API unavailable.");
}
}
// Convert from logical to physical x coordinates
Canvas2DChart.prototype.x = function (v) {
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
};
// Convert from logical to physical y coordinates
Canvas2DChart.prototype.y = function (v) {
return this.height -
((v - this.origin[1]) / this.dimensions[1]) * this.height;
};
// Set the color to be used for drawing operations
Canvas2DChart.prototype.setColor = function (color) {
var mappedColor = color.map(function (c, i) {
return i < 3 ? Math.floor(c * 255) : (c);
}).join(',');
this.c2d.strokeStyle = "rgba(" + mappedColor + ")";
this.c2d.fillStyle = "rgba(" + mappedColor + ")";
};
Canvas2DChart.prototype.clear = function () {
var canvas = this.canvas;
this.width = canvas.width;
this.height = canvas.height;
this.c2d.clearRect(0, 0, this.width, this.height);
};
Canvas2DChart.prototype.setDimensions = function (newDimensions, newOrigin) {
this.dimensions = newDimensions;
this.origin = newOrigin;
};
Canvas2DChart.prototype.drawLine = function (buf, color, points) {
var i;
this.setColor(color);
// Configure context to draw two-pixel-thick lines
this.c2d.lineWidth = 2;
// Start a new path...
if (buf.length > 1) {
this.c2d.beginPath();
this.c2d.moveTo(this.x(buf[0]), this.y(buf[1]));
}
// ...and add points to it...
for (i = 2; i < points * 2; i = i + 2) {
this.c2d.lineTo(this.x(buf[i]), this.y(buf[i + 1]));
}
// ...before finally drawing it.
this.c2d.stroke();
};
Canvas2DChart.prototype.drawSquare = function (min, max, color) {
var x1 = this.x(min[0]),
y1 = this.y(min[1]),
w = this.x(max[0]) - x1,
h = this.y(max[1]) - y1;
this.setColor(color);
this.c2d.fillRect(x1, y1, w, h);
};
return Canvas2DChart;
}
);

View File

@ -1,160 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* Module defining GLPlot. Created by vwoeltje on 11/12/14.
*/
define(
[],
function () {
// WebGL shader sources (for drawing plain colors)
var FRAGMENT_SHADER = [
"precision mediump float;",
"uniform vec4 uColor;",
"void main(void) {",
"gl_FragColor = uColor;",
"}"
].join('\n'),
VERTEX_SHADER = [
"attribute vec2 aVertexPosition;",
"uniform vec2 uDimensions;",
"uniform vec2 uOrigin;",
"void main(void) {",
"gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
"}"
].join('\n');
/**
* Create a new chart which uses WebGL for rendering.
*
* @memberof platform/features/plot
* @constructor
* @implements {platform/features/plot.Chart}
* @param {CanvasElement} canvas the canvas object to render upon
* @throws {Error} an error is thrown if WebGL is unavailable.
*/
function GLChart(canvas) {
var gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }) ||
canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }),
vertexShader,
fragmentShader,
program,
aVertexPosition,
uColor,
uDimensions,
uOrigin;
// Ensure a context was actually available before proceeding
if (!gl) {
throw new Error("WebGL unavailable.");
}
// Initialize shaders
vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, VERTEX_SHADER);
gl.compileShader(vertexShader);
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
gl.compileShader(fragmentShader);
// Assemble vertex/fragment shaders into programs
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
// Get locations for attribs/uniforms from the
// shader programs (to pass values into shaders at draw-time)
aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
uColor = gl.getUniformLocation(program, "uColor");
uDimensions = gl.getUniformLocation(program, "uDimensions");
uOrigin = gl.getUniformLocation(program, "uOrigin");
gl.enableVertexAttribArray(aVertexPosition);
// Create a buffer to holds points which will be drawn
this.buffer = gl.createBuffer();
// Use a line width of 2.0 for legibility
gl.lineWidth(2.0);
// Enable blending, for smoothness
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
this.gl = gl;
this.aVertexPosition = aVertexPosition;
this.uColor = uColor;
this.uDimensions = uDimensions;
this.uOrigin = uOrigin;
}
// Utility function to handle drawing of a buffer;
// drawType will determine whether this is a box, line, etc.
GLChart.prototype.doDraw = function (drawType, buf, color, points) {
var gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
gl.uniform4fv(this.uColor, color);
gl.drawArrays(drawType, 0, points);
};
GLChart.prototype.clear = function () {
var gl = this.gl;
// Set the viewport size; note that we use the width/height
// that our WebGL context reports, which may be lower
// resolution than the canvas we requested.
gl.viewport(
0,
0,
gl.drawingBufferWidth,
gl.drawingBufferHeight
);
gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
};
GLChart.prototype.setDimensions = function (dimensions, origin) {
var gl = this.gl;
if (dimensions && dimensions.length > 0 &&
origin && origin.length > 0) {
gl.uniform2fv(this.uDimensions, dimensions);
gl.uniform2fv(this.uOrigin, origin);
}
};
GLChart.prototype.drawLine = function (buf, color, points) {
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
};
GLChart.prototype.drawSquare = function (min, max, color) {
this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
), color, 4);
};
return GLChart;
}
);

View File

@ -1,250 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
*/
define(
["./GLChart", "./Canvas2DChart"],
function (GLChart, Canvas2DChart) {
var TEMPLATE = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
/**
* The mct-chart directive provides a canvas element which can be
* drawn upon, to support Plot view and similar visualizations.
*
* This directive takes one attribute, "draw", which is an Angular
* expression which will be two-way bound to a drawing object. This
* drawing object should contain:
*
* * `dimensions`: An object describing the logical bounds of the
* drawable area, containing two fields:
* * `origin`: The position, in logical coordinates, of the
* lower-left corner of the chart area. A two-element array.
* * `dimensions`: A two-element array containing the width
* and height of the chart area, in logical coordinates.
* * `lines`: An array of lines to be drawn, where each line is
* expressed as an object containing:
* * `buffer`: A Float32Array containing points in the line,
* in logical coordinate, in sequential x/y pairs.
* * `color`: The color of the line, as a four-element RGBA
* array, where each element is in the range of 0.0-1.0
* * `points`: The number of points in the line.
* * `boxes`: An array of rectangles to draw in the chart area
* (used for marquee zoom). Each is an object containing:
* * `start`: The first corner of the rectangle (as a two-element
* array, logical coordinates)
* * `end`: The opposite corner of the rectangle (again, as a
* two-element array)
* * `color`: The color of the box, as a four-element RGBA
* array, where each element is in the range of 0.0-1.0
*
* @memberof platform/features/plot
* @constructor
*/
function MCTChart($interval, $log) {
// Get an underlying chart implementation
function getChart(Charts, canvas) {
// Try the first available option...
var Chart = Charts[0];
// This function recursively try-catches all options;
// if these all fail, issue a warning.
if (!Chart) {
$log.warn("Cannot initialize mct-chart.");
return undefined;
}
// Try first option; if it fails, try remaining options
try {
return new Chart(canvas);
} catch (e) {
$log.warn([
"Could not instantiate chart",
Chart.name,
";",
e.message
].join(" "));
return getChart(Charts.slice(1), canvas);
}
}
function linkChart(scope, element) {
var canvas = element.find("canvas")[0],
activeInterval,
chart;
// Handle drawing, based on contents of the "draw" object
// in scope
function doDraw(draw) {
// Ensure canvas context has same resolution
// as canvas element
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// Clear previous contents
chart.clear();
// Nothing to draw if no draw object defined
if (!draw) {
return;
}
// Set logical boundaries for the chart
chart.setDimensions(
draw.dimensions || [1, 1],
draw.origin || [0, 0]
);
// Draw line segments
(draw.lines || []).forEach(function (line) {
chart.drawLine(
line.buffer,
line.color,
line.points
);
});
// Draw boxes (e.g. marquee zoom rect)
(draw.boxes || []).forEach(function (box) {
chart.drawSquare(
box.start,
box.end,
box.color
);
});
}
// Issue a drawing call, if-and-only-if canvas size
// has changed. This will be called on a timer, since
// there is no event to depend on.
function drawIfResized() {
if (canvas.width !== canvas.offsetWidth ||
canvas.height !== canvas.offsetHeight) {
doDraw(scope.draw);
scope.$apply();
}
}
// Stop watching for changes to size (scope destroyed)
function releaseInterval() {
if (activeInterval) {
$interval.cancel(activeInterval);
}
}
// Switch from WebGL to plain 2D if context is lost
function fallbackFromWebGL() {
element.html(TEMPLATE);
canvas = element.find("canvas")[0];
chart = getChart([Canvas2DChart], canvas);
if (chart) {
doDraw(scope.draw);
}
}
// Try to initialize a chart.
chart = getChart([GLChart, Canvas2DChart], canvas);
// If that failed, there's nothing more we can do here.
// (A warning will already have been issued)
if (!chart) {
return;
}
// WebGL is a bit of a special case; it may work, then fail
// later for various reasons, so we need to listen for this
// and fall back to plain canvas drawing when it occurs.
canvas.addEventListener("webglcontextlost", fallbackFromWebGL);
// Check for resize, on a timer
activeInterval = $interval(drawIfResized, 1000, 0, false);
// Watch "draw" for external changes to the set of
// things to be drawn.
scope.$watchCollection("draw", doDraw);
// Stop checking for resize when scope is destroyed
scope.$on("$destroy", releaseInterval);
}
return {
// Apply directive only to elements
restrict: "E",
// Template to use (a canvas element)
template: TEMPLATE,
// Link function; set up scope
link: linkChart,
// Initial, isolate scope for the directive
scope: { draw: "=" }
};
}
/**
* @interface platform/features/plot.Chart
* @private
*/
/**
* Clear the chart.
* @method platform/features/plot.Chart#clear
*/
/**
* Set the logical boundaries of the chart.
* @param {number[]} dimensions the horizontal and
* vertical dimensions of the chart
* @param {number[]} origin the horizontal/vertical
* origin of the chart
* @memberof platform/features/plot.Chart#setDimensions
*/
/**
* Draw the supplied buffer as a line strip (a sequence
* of line segments), in the chosen color.
* @param {Float32Array} buf the line strip to draw,
* in alternating x/y positions
* @param {number[]} color the color to use when drawing
* the line, as an RGBA color where each element
* is in the range of 0.0-1.0
* @param {number} points the number of points to draw
* @memberof platform/features/plot.Chart#drawLine
*/
/**
* Draw a rectangle extending from one corner to another,
* in the chosen color.
* @param {number[]} min the first corner of the rectangle
* @param {number[]} max the opposite corner
* @param {number[]} color the color to use when drawing
* the rectangle, as an RGBA color where each element
* is in the range of 0.0-1.0
* @memberof platform/features/plot.Chart#drawSquare
*/
return MCTChart;
}
);

View File

@ -1,437 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* This bundle adds a "Plot" view for numeric telemetry data.
* @namespace platform/features/plot
*/
define(
[
"./elements/PlotUpdater",
"./elements/PlotPalette",
"./elements/PlotAxis",
"./elements/PlotLimitTracker",
"./elements/PlotTelemetryFormatter",
"./modes/PlotModeOptions",
"./SubPlotFactory"
],
function (
PlotUpdater,
PlotPalette,
PlotAxis,
PlotLimitTracker,
PlotTelemetryFormatter,
PlotModeOptions,
SubPlotFactory
) {
var AXIS_DEFAULTS = [
{ "name": "Time" },
{ "name": "Value" }
];
/**
* The PlotController is responsible for any computation/logic
* associated with displaying the plot view. Specifically, these
* responsibilities include:
*
* * Describing axes and labeling.
* * Handling user interactions.
* * Deciding what needs to be drawn in the chart area.
*
* @memberof platform/features/plot
* @constructor
*/
function PlotController(
$scope,
$element,
exportImageService,
telemetryFormatter,
telemetryHandler,
throttle,
PLOT_FIXED_DURATION,
openmct
) {
var self = this,
plotTelemetryFormatter =
new PlotTelemetryFormatter(telemetryFormatter),
subPlotFactory =
new SubPlotFactory(plotTelemetryFormatter),
cachedObjects = [],
updater,
lastBounds,
lastRange,
lastDomain,
handle;
var timeAPI = openmct.time;
// Populate the scope with axis information (specifically, options
// available for each axis.)
function setupAxes(metadatas) {
$scope.axes.forEach(function (axis) {
axis.updateMetadata(metadatas);
});
}
// Trigger an update of a specific subplot;
// used in a loop to update all subplots.
function updateSubplot(subplot) {
subplot.update();
}
// Set up available modes (stacked/overlaid), based on the
// set of telemetry objects in this plot view.
function setupModes(telemetryObjects) {
if (cachedObjects !== telemetryObjects) {
cachedObjects = telemetryObjects;
self.modeOptions = new PlotModeOptions(
telemetryObjects || [],
subPlotFactory
);
}
}
// Change the displayable bounds
function setBasePanZoom(bounds) {
var start = bounds.start,
end = bounds.end;
if (updater) {
updater.setDomainBounds(start, end);
self.update();
}
lastBounds = bounds;
}
// Reinstantiate the plot updater (e.g. because we have a
// new subscription.) This will clear the plot.
function recreateUpdater() {
var domain = $scope.axes[0].active.key,
range = $scope.axes[1].active.key,
duration = PLOT_FIXED_DURATION;
updater = new PlotUpdater(handle, domain, range, duration);
lastDomain = domain;
lastRange = range;
self.limitTracker = new PlotLimitTracker(handle, range);
// Keep any externally-provided bounds
if (lastBounds) {
setBasePanZoom(lastBounds);
}
}
function getUpdater() {
if (!updater) {
recreateUpdater();
}
return updater;
}
// Handle new telemetry data in this plot
function updateValues() {
self.pending = false;
if (handle) {
setupModes(handle.getTelemetryObjects());
setupAxes(handle.getMetadata());
getUpdater().update();
self.modeOptions.getModeHandler().plotTelemetry(updater);
self.limitTracker.update();
self.update();
}
}
// Display new historical data as it becomes available
function addHistoricalData(domainObject, series) {
self.pending = false;
getUpdater().addHistorical(domainObject, series);
self.modeOptions.getModeHandler().plotTelemetry(updater);
self.update();
}
// Issue a new request for historical telemetry
function requestTelemetry() {
if (handle) {
handle.request({}, addHistoricalData);
}
}
// Requery for data entirely
function replot() {
if (handle) {
updater = undefined;
requestTelemetry();
}
}
function changeTimeOfInterest(timeOfInterest) {
if (timeOfInterest !== undefined) {
var bounds = timeAPI.bounds();
var range = bounds.end - bounds.start;
$scope.toiPerc = ((timeOfInterest - bounds.start) / range) * 100;
$scope.toiPinned = true;
} else {
$scope.toiPerc = undefined;
$scope.toiPinned = false;
}
}
// Create a new subscription; telemetrySubscriber gets
// to do the meaningful work here.
function subscribe(domainObject) {
if (handle) {
handle.unsubscribe();
}
handle = domainObject && telemetryHandler.handle(
domainObject,
updateValues,
true // Lossless
);
replot();
changeTimeOfInterest(timeAPI.timeOfInterest());
timeAPI.on("timeOfInterest", changeTimeOfInterest);
}
// Release the current subscription (called when scope is destroyed)
function releaseSubscription() {
if (handle) {
handle.unsubscribe();
handle = undefined;
}
timeAPI.off("timeOfInterest", changeTimeOfInterest);
}
function requery() {
self.pending = true;
releaseSubscription();
subscribe($scope.domainObject);
}
function updateDomainFormat() {
var domainAxis = $scope.axes[0];
plotTelemetryFormatter
.setDomainFormat(domainAxis.active.format);
}
function domainRequery(newDomain) {
if (newDomain !== lastDomain) {
updateDomainFormat();
requery();
}
}
function rangeRequery(newRange) {
if (newRange !== lastRange) {
requery();
}
}
// Respond to a display bounds change (requery for data)
function changeDisplayBounds(event, bounds, follow) {
//'hack' for follow mode
if (follow === true) {
setBasePanZoom(bounds);
} else {
var domainAxis = $scope.axes[0];
if (bounds.domain) {
domainAxis.chooseOption(bounds.domain);
}
updateDomainFormat();
setBasePanZoom(bounds);
requery();
}
self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
changeTimeOfInterest(timeAPI.timeOfInterest());
}
this.modeOptions = new PlotModeOptions([], subPlotFactory);
this.updateValues = updateValues;
// Create a throttled update function
this.scheduleUpdate = throttle(function () {
self.modeOptions.getModeHandler().getSubPlots()
.forEach(updateSubplot);
});
self.pending = true;
self.$element = $element;
self.exportImageService = exportImageService;
// Initialize axes; will get repopulated when telemetry
// metadata becomes available.
$scope.axes = [
new PlotAxis("domains", [], AXIS_DEFAULTS[0]),
new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
];
//Are some initialized bounds defined?
var bounds = timeAPI.bounds();
if (bounds &&
bounds.start !== undefined &&
bounds.end !== undefined) {
changeDisplayBounds(undefined, timeAPI.bounds(), timeAPI.clock() !== undefined);
}
// Watch for changes to the selected axis
$scope.$watch("axes[0].active.key", domainRequery);
$scope.$watch("axes[1].active.key", rangeRequery);
// Subscribe to telemetry when a domain object becomes available
$scope.$watch('domainObject', subscribe);
// Respond to external bounds changes
$scope.$on("telemetry:display:bounds", changeDisplayBounds);
// Unsubscribe when the plot is destroyed
$scope.$on("$destroy", releaseSubscription);
}
/**
* Get the color (as a style-friendly string) to use
* for plotting the trace at the specified index.
* @param {number} index the index of the trace
* @returns {string} the color, in #RRGGBB form
*/
PlotController.prototype.getColor = function (index) {
return PlotPalette.getStringColor(index);
};
/**
* Check if the plot is zoomed or panned out
* of its default state (to determine whether back/unzoom
* controls should be shown)
* @returns {boolean} true if not in default state
*/
PlotController.prototype.isZoomed = function () {
return this.modeOptions.getModeHandler().isZoomed();
};
/**
* Undo the most recent pan/zoom change and restore
* the prior state.
*/
PlotController.prototype.stepBackPanZoom = function () {
return this.modeOptions.getModeHandler().stepBackPanZoom();
};
/**
* Undo all pan/zoom changes and restore the initial state.
*/
PlotController.prototype.unzoom = function () {
return this.modeOptions.getModeHandler().unzoom();
};
/**
* Get the mode options (Stacked/Overlaid) that are applicable
* for this plot.
*/
PlotController.prototype.getModeOptions = function () {
return this.modeOptions.getModeOptions();
};
/**
* Get the current mode that is applicable to this plot. This
* will include key, name, and cssClass fields.
*/
PlotController.prototype.getMode = function () {
return this.modeOptions.getMode();
};
/**
* Set the mode which should be active in this plot.
* @param mode one of the mode options returned from
* getModeOptions()
*/
PlotController.prototype.setMode = function (mode) {
this.modeOptions.setMode(mode);
this.updateValues();
};
/**
* Get all individual plots contained within this Plot view.
* (Multiple may be contained when in Stacked mode).
* @returns {SubPlot[]} all subplots in this Plot view
*/
PlotController.prototype.getSubPlots = function () {
return this.modeOptions.getModeHandler().getSubPlots();
};
/**
* Get the CSS class to apply to the legend for this domain
* object; this will reflect limit state.
* @returns {string} the CSS class
*/
PlotController.prototype.getLegendClass = function (telemetryObject) {
return this.limitTracker &&
this.limitTracker.getLegendClass(telemetryObject);
};
/**
* Explicitly update all plots.
*/
PlotController.prototype.update = function () {
this.scheduleUpdate();
};
/**
* Check if a request is pending (to show the wait spinner)
*/
PlotController.prototype.isRequestPending = function () {
// Placeholder; this should reflect request state
// when requesting historical telemetry
return this.pending;
};
PlotController.prototype.setUnsynchedStatus = function (domainObject, status) {
if (domainObject.hasCapability('status')) {
domainObject.getCapability('status').set('timeconductor-unsynced', status);
}
};
/**
* Export the plot to PNG
*/
PlotController.prototype.exportPNG = function () {
var self = this;
self.hideExportButtons = true;
self.exportImageService.exportPNG(self.$element[0], "plot.png").finally(function () {
self.hideExportButtons = false;
});
};
/**
* Export the plot to JPG
*/
PlotController.prototype.exportJPG = function () {
var self = this;
self.hideExportButtons = true;
self.exportImageService.exportJPG(self.$element[0], "plot.jpg").finally(function () {
self.hideExportButtons = false;
});
};
return PlotController;
}
);

View File

@ -1,195 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
['./PlotOptionsForm'],
function (PlotOptionsForm) {
/**
* Notes on implementation of plot options
*
* Multiple y-axes will have to be handled with multiple forms as
* they will need to be stored on distinct model object
*
* Likewise plot series options per-child will need to be separate
* forms.
*/
/**
* The LayoutController is responsible for supporting the
* Layout view. It arranges frames according to saved configuration
* and provides methods for updating these based on mouse
* movement.
* @memberof platform/features/plot
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function PlotOptionsController($scope) {
var self = this;
this.$scope = $scope;
this.domainObject = $scope.domainObject;
this.configuration = this.domainObject.getModel().configuration || {};
this.plotOptionsForm = new PlotOptionsForm();
this.composition = [];
this.watches = [];
/*
Listen for changes to the domain object and update the object's
children.
*/
this.mutationListener = this.domainObject.getCapability('mutation').listen(function (model) {
if (self.hasCompositionChanged(self.composition, model.composition)) {
self.updateChildren();
}
});
/*
Set form structures on scope
*/
$scope.plotSeriesForm = this.plotOptionsForm.plotSeriesForm;
$scope.xAxisForm = this.plotOptionsForm.xAxisForm;
$scope.yAxisForm = this.plotOptionsForm.yAxisForm;
$scope.$on("$destroy", function () {
//Clean up any listeners on destruction of controller
self.mutationListener();
});
this.defaultConfiguration();
this.updateChildren();
/*
* Setup a number of watches for changes to form values. On
* change, update the model configuration via mutation
*/
$scope.$watchCollection('configuration.plot.yAxis', function (newValue, oldValue) {
self.updateConfiguration(newValue, oldValue);
});
$scope.$watchCollection('configuration.plot.xAxis', function (newValue, oldValue) {
self.updateConfiguration(newValue, oldValue);
});
this.watchSeries();
}
/**
* Unregister all watches for series data (ie. the configuration for
* child objects)
* @private
*/
PlotOptionsController.prototype.clearSeriesWatches = function () {
this.watches.forEach(function (watch) {
watch();
});
this.watches = [];
};
/**
* Attach watches for each object in the plot's composition
* @private
*/
PlotOptionsController.prototype.watchSeries = function () {
var self = this;
this.clearSeriesWatches();
(self.$scope.children || []).forEach(function (child, index) {
self.watches.push(
self.$scope.$watchCollection(
'configuration.plot.series[' + index + ']',
function (newValue, oldValue) {
self.updateConfiguration(newValue, oldValue);
}
)
);
});
};
/**
* Determine whether the changes to the model that triggered a
* mutation event were purely compositional.
*
* @private
*/
PlotOptionsController.prototype.hasCompositionChanged = function (oldComposition, newComposition) {
// Framed slightly strangely, but the boolean logic is
// easier to follow for the unchanged case.
var isUnchanged = oldComposition === newComposition ||
(
oldComposition.length === newComposition.length &&
oldComposition.every(function (currentValue, index) {
return newComposition[index] && currentValue === newComposition[index];
})
);
return !isUnchanged;
};
/**
* Default the plot options model
*
* @private
*/
PlotOptionsController.prototype.defaultConfiguration = function () {
this.configuration.plot = this.configuration.plot || {};
this.configuration.plot.xAxis = this.configuration.plot.xAxis || {};
this.configuration.plot.yAxis = this.configuration.plot.yAxis || {}; // y-axes will be associative array keyed on axis key
this.configuration.plot.series = this.configuration.plot.series || []; // series will be associative array keyed on sub-object id
this.$scope.configuration = this.configuration;
};
/**
* When a child is added to, or removed from a plot, update the
* plot options model
* @private
*/
PlotOptionsController.prototype.updateChildren = function () {
var self = this;
this.domainObject.useCapability('composition').then(function (children) {
self.$scope.children = children;
self.composition = self.domainObject.getModel().composition;
children.forEach(function (child, index) {
self.configuration.plot.series[index] =
self.configuration.plot.series[index] || {'id': child.getId()};
});
self.watchSeries();
});
};
/**
* On changes to the form, update the configuration on the domain
* object
* @private
*/
PlotOptionsController.prototype.updateConfiguration = function () {
var self = this;
this.domainObject.useCapability('mutation', function (model) {
model.configuration = model.configuration || {};
model.configuration.plot = self.configuration.plot;
});
};
return PlotOptionsController;
}
);

View File

@ -1,150 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* A class for encapsulating structure and behaviour of the plot
* options form
* @memberOf platform/features/plot
* @param topic
* @constructor
*/
function PlotOptionsForm() {
/*
Defined below are the form structures for the plot options.
*/
this.xAxisForm = {
'name': 'x-axis',
'sections': [{
'name': 'x-axis',
'rows': [
{
'name': 'Domain',
'control': 'select',
'key': 'key',
'options': [
{'name': 'SCET', 'value': 'scet'},
{'name': 'SCLK', 'value': 'sclk'},
{'name': 'LST', 'value': 'lst'}
]
}
]
}]};
this.yAxisForm = {
'name': 'y-axis',
'sections': [{
// Will need to be repeated for each y-axis, with a
// distinct name for each. Ideally the name of the axis
// itself.
'name': 'y-axis',
'rows': [
{
'name': 'Range',
'control': 'select',
'key': 'key',
'options': [
{'name': 'EU', 'value': 'eu'},
{'name': 'DN', 'value': 'dn'},
{'name': 'Status', 'value': 'status'}
]
},
{
'name': 'Autoscale',
'control': 'checkbox',
'key': 'autoscale'
},
{
'name': 'Min',
'control': 'textfield',
'key': 'min',
'pattern': '[0-9]',
'inputsize' : 'sm'
},
{
'name': 'Max',
'control': 'textfield',
'key': 'max',
'pattern': '[0-9]',
'inputsize' : 'sm'
}
]
}]
};
this.plotSeriesForm = {
'name': 'Series Options',
'sections': [
{
rows: [
{
'name': 'Color',
'control': 'color',
'key': 'color'
}]
},
{
'rows': [
{
'name': 'Markers',
'control': 'checkbox',
'key': 'markers',
'layout': 'control-first'
}
]
},
{
'rows': [
{
'name': 'No Line',
'control': 'radio',
'key': 'lineType',
'value': 'noLine',
'layout': 'control-first'
},
{
'name': 'Step Line',
'control': 'radio',
'key': 'lineType',
'value': 'stepLine',
'layout': 'control-first'
},
{
'name': 'Linear Line',
'control': 'radio',
'key': 'lineType',
'value': 'linearLine',
'layout': 'control-first'
}
]
}
]
};
}
return PlotOptionsForm;
}
);

View File

@ -1,415 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[
'./elements/PlotPosition',
'./elements/PlotTickGenerator'
],
function (PlotPosition, PlotTickGenerator) {
var DOMAIN_TICKS = 5,
RANGE_TICKS = 7;
/**
* A SubPlot is an individual plot within a Plot View (which
* may contain multiple plots, specifically when in Stacked
* plot mode.)
* @memberof platform/features/plot
* @constructor
* @param {DomainObject[]} telemetryObjects the domain objects
* which will be plotted in this sub-plot
* @param {PlotPanZoomStack} panZoomStack the stack of pan-zoom
* states which is applicable to this sub-plot
* @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service; used to convert domain/range values
* from telemetry data sets to a human-readable form.
*/
function SubPlot(telemetryObjects, panZoomStack, telemetryFormatter) {
// We are used from a template often, so maintain
// state in local variables to allow for fast look-up,
// as is normal for controllers.
this.telemetryObjects = telemetryObjects;
this.domainTicks = [];
this.rangeTicks = [];
this.formatter = telemetryFormatter;
this.draw = {};
this.hovering = false;
this.panZoomStack = panZoomStack;
// Start with the right initial drawing bounds,
// tick marks
this.updateDrawingBounds();
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.
function isNonEmpty(v) {
return typeof v === 'string' && v !== "";
}
// Converts from pixel coordinates to domain-range,
// to interpret mouse gestures.
SubPlot.prototype.mousePositionToDomainRange = function (mousePosition) {
return new PlotPosition(
mousePosition.x,
mousePosition.y,
mousePosition.width,
mousePosition.height,
this.panZoomStack
).getPosition();
};
// Utility function to get the mouse position (in x,y
// pixel coordinates in the canvas area) from a mouse
// event object.
SubPlot.prototype.toMousePosition = function ($event) {
var bounds = this.subPlotBounds;
return {
x: $event.clientX - bounds.left,
y: $event.clientY - bounds.top,
width: bounds.width,
height: bounds.height
};
};
// Convert a domain-range position to a displayable
// position. This will subtract the domain offset, which
// is used to bias domain values to minimize loss-of-precision
// associated with conversion to a 32-bit floating point
// format (which is needed in the chart area itself, by WebGL.)
SubPlot.prototype.toDisplayable = function (position) {
return [position[0] - this.domainOffset, position[1]];
};
// Update the current hover coordinates
SubPlot.prototype.updateHoverCoordinates = function () {
var formatter = this.formatter;
// Utility, for map/forEach loops. Index 0 is domain,
// index 1 is range.
function formatValue(v, i) {
return i ?
formatter.formatRangeValue(v) :
formatter.formatDomainValue(v);
}
this.hoverCoordinates = this.mousePosition &&
this.mousePositionToDomainRange(this.mousePosition)
.map(formatValue)
.filter(isNonEmpty)
.join(", ");
};
// Update the drawable marquee area to reflect current
// mouse position (or don't show it at all, if no marquee
// zoom is in progress)
SubPlot.prototype.updateMarqueeBox = function () {
// Express this as a box in the draw object, which
// is passed to an mct-chart in the template for rendering.
this.draw.boxes = this.marqueeStart ?
[{
start: this.toDisplayable(
this.mousePositionToDomainRange(this.marqueeStart)
),
end: this.toDisplayable(
this.mousePositionToDomainRange(this.mousePosition)
),
color: [1, 1, 1, 0.5]
}] : undefined;
};
// Update the bounds (origin and dimensions) of the drawing area.
SubPlot.prototype.updateDrawingBounds = function () {
var panZoom = this.panZoomStack.getPanZoom();
// Communicate pan-zoom state from stack to the draw object
// which is passed to mct-chart in the template.
this.draw.dimensions = panZoom.dimensions;
this.draw.origin = [
panZoom.origin[0] - this.domainOffset,
panZoom.origin[1]
];
};
// Update tick marks in scope.
SubPlot.prototype.updateTicks = function () {
var tickGenerator =
new PlotTickGenerator(this.panZoomStack, this.formatter);
this.domainTicks =
tickGenerator.generateDomainTicks(DOMAIN_TICKS);
this.rangeTicks =
tickGenerator.generateRangeTicks(RANGE_TICKS);
};
SubPlot.prototype.updatePan = function () {
var start, current, delta, nextOrigin;
// Clear the previous panning pan-zoom state
this.panZoomStack.popPanZoom();
// Calculate what the new resulting pan-zoom should be
start = this.mousePositionToDomainRange(
this.panStart,
this.panZoomStack
);
current = this.mousePositionToDomainRange(
this.mousePosition,
this.panZoomStack
);
delta = [current[0] - start[0], current[1] - start[1]];
nextOrigin = [
this.panStartBounds.origin[0] - delta[0],
this.panStartBounds.origin[1] - delta[1]
];
// ...and push a new one at the current mouse position
this.panZoomStack
.pushPanZoom(nextOrigin, this.panStartBounds.dimensions);
};
/**
* Get the set of domain objects which are being
* represented in this sub-plot.
* @returns {DomainObject[]} the domain objects which
* will have data plotted in this sub-plot
*/
SubPlot.prototype.getTelemetryObjects = function () {
return this.telemetryObjects;
};
/**
* Get ticks mark information appropriate for using in the
* template for this sub-plot's domain axis, as prepared
* by the PlotTickGenerator.
* @returns {Array} tick marks for the domain axis
*/
SubPlot.prototype.getDomainTicks = function () {
return this.domainTicks;
};
/**
* Get ticks mark information appropriate for using in the
* template for this sub-plot's range axis, as prepared
* by the PlotTickGenerator.
* @returns {Array} tick marks for the range axis
*/
SubPlot.prototype.getRangeTicks = function () {
return this.rangeTicks;
};
/**
* Get the drawing object associated with this sub-plot;
* this object will be passed to the mct-chart in which
* this sub-plot's lines will be plotted, as its "draw"
* attribute, and should have the same internal format
* expected by that directive.
* @return {object} the drawing object
*/
SubPlot.prototype.getDrawingObject = function () {
return this.draw;
};
/**
* Get the coordinates (as displayable text) for the
* current mouse position.
* @returns {string[]} the displayable domain and range
* coordinates over which the mouse is hovered
*/
SubPlot.prototype.getHoverCoordinates = function () {
return this.hoverCoordinates;
};
/**
* Handle mouse movement over the chart area.
* @param $event the mouse event
* @memberof platform/features/plot.SubPlot#
*/
SubPlot.prototype.hover = function ($event) {
this.hovering = true;
this.subPlotBounds = $event.target.getBoundingClientRect();
this.mousePosition = this.toMousePosition($event);
//If there is a domain to display, show hover coordinates, otherwise hover coordinates are meaningless
if (this.hasDomainData()) {
this.updateHoverCoordinates();
}
if (this.marqueeStart) {
this.updateMarqueeBox();
}
if (this.panStart) {
this.updatePan();
this.updateDrawingBounds();
this.updateTicks();
}
};
/**
* Continue a previously-start pan or zoom gesture.
* @param $event the mouse event
* @memberof platform/features/plot.SubPlot#
*/
SubPlot.prototype.continueDrag = function ($event) {
this.mousePosition = this.toMousePosition($event);
if (this.marqueeStart) {
this.updateMarqueeBox();
}
if (this.panStart) {
this.updatePan();
this.updateDrawingBounds();
this.updateTicks();
}
};
/**
* Initiate a marquee zoom action.
* @param $event the mouse event
*/
SubPlot.prototype.startDrag = function ($event) {
this.subPlotBounds = $event.target.getBoundingClientRect();
this.mousePosition = this.toMousePosition($event);
// Treat any modifier key as a pan
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
// Start panning
this.panStart = this.mousePosition;
this.panStartBounds = this.panZoomStack.getPanZoom();
// We're starting a pan, so add this back as a
// state on the stack; it will get replaced
// during the pan.
this.panZoomStack.pushPanZoom(
this.panStartBounds.origin,
this.panStartBounds.dimensions
);
$event.preventDefault();
} else {
// Start marquee zooming
this.marqueeStart = this.mousePosition;
this.updateMarqueeBox();
}
};
/**
* Complete a marquee zoom action.
* @param $event the mouse event
*/
SubPlot.prototype.endDrag = function ($event) {
var self = this;
// Perform a marquee zoom.
function marqueeZoom(start, end) {
// Determine what boundary is described by the marquee,
// in domain-range values. Use the minima for origin, so that
// it doesn't matter what direction the user marqueed in.
var a = self.mousePositionToDomainRange(start),
b = self.mousePositionToDomainRange(end),
origin = [
Math.min(a[0], b[0]),
Math.min(a[1], b[1])
],
dimensions = [
Math.max(a[0], b[0]) - origin[0],
Math.max(a[1], b[1]) - origin[1]
];
// Proceed with zoom if zoom dimensions are non zeros
if (!(dimensions[0] === 0 && dimensions[1] === 0)) {
// Push the new state onto the pan-zoom stack
self.panZoomStack.pushPanZoom(origin, dimensions);
// Make sure tick marks reflect new bounds
self.updateTicks();
}
}
this.mousePosition = this.toMousePosition($event);
this.subPlotBounds = undefined;
if (this.marqueeStart) {
marqueeZoom(this.marqueeStart, this.mousePosition);
this.marqueeStart = undefined;
this.updateMarqueeBox();
this.updateDrawingBounds();
this.updateTicks();
}
if (this.panStart) {
// End panning
this.panStart = undefined;
this.panStartBounds = undefined;
}
};
/**
* Update the drawing bounds, marquee box, and
* tick marks for this subplot.
*/
SubPlot.prototype.update = function () {
this.updateDrawingBounds();
this.updateMarqueeBox();
this.updateTicks();
};
/**
* Set the domain offset associated with this sub-plot.
* A domain offset is subtracted from all domain
* before lines are drawn to avoid artifacts associated
* with the use of 32-bit floats when domain values
* are often timestamps (due to insufficient precision.)
* A SubPlot will be drawing boxes (for marquee zoom) in
* the same offset coordinate space, so it needs to know
* the value of this to position that marquee box
* correctly.
* @param {number} value the domain offset
*/
SubPlot.prototype.setDomainOffset = function (value) {
this.domainOffset = value;
};
/**
* When used with no argument, check whether or not the user
* is currently hovering over this subplot. When used with
* an argument, set that state.
* @param {boolean} [state] the new hovering state
* @returns {boolean} the hovering state
*/
SubPlot.prototype.isHovering = function (state) {
if (state !== undefined) {
this.hovering = state;
}
return this.hovering;
};
return SubPlot;
}
);

View File

@ -1,134 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* A PlotAxis provides a template-ready set of options
* for the domain or range axis, sufficient to populate
* selectors.
*
* @memberof platform/features/plot
* @constructor
* @param {string} axisType the field in metadatas to
* look at for axis options; usually one of
* "domains" or "ranges"
* @param {object[]} metadatas metadata objects, as
* returned by the `getMetadata()` method of
* a `telemetry` capability.
* @param {object} defaultValue the value to use for the
* active state in the event that no options are
* found; should contain "name" and "key" at
* minimum.
*
*/
function PlotAxis(axisType, metadatas, defaultValue) {
this.axisType = axisType;
this.defaultValue = defaultValue;
this.optionKeys = {};
/**
* The currently chosen option for this axis. An
* initial value is provided; this will be updated
* directly form the plot template.
* @memberof platform/features/plot.PlotAxis#
*/
this.active = defaultValue;
/**
* The set of options applicable for this axis;
* an array of objects, where each object contains a
* "key" field and a "name" field (for machine- and
* human-readable names respectively)
* @memberof platform/features/plot.PlotAxis#
*/
this.options = [];
// Initialize options from metadata objects
this.updateMetadata(metadatas);
}
/**
* Update axis options to reflect current metadata.
* @param {TelemetryMetadata[]} metadata objects describing
* applicable telemetry
*/
PlotAxis.prototype.updateMetadata = function (metadatas) {
var axisType = this.axisType,
optionKeys = this.optionKeys,
newOptions = {},
toAdd = [];
function isValid(option) {
return option && optionKeys[option.key];
}
metadatas.forEach(function (m) {
(m[axisType] || []).forEach(function (option) {
var key = option.key;
if (!optionKeys[key] && !newOptions[key]) {
toAdd.push(option);
}
newOptions[key] = true;
});
});
optionKeys = this.optionKeys = newOptions;
// General approach here is to avoid changing object
// instances unless something has really changed, since
// Angular is watching; don't want to trigger extra digests.
if (!this.options.every(isValid)) {
this.options = this.options.filter(isValid);
}
if (toAdd.length > 0) {
this.options = this.options.concat(toAdd);
}
if (!isValid(this.active)) {
this.active = this.options[0] || this.defaultValue;
}
};
/**
* Change the domain/range selection for this axis. If the
* provided `key` is not recognized as an option, no change
* will occur.
* @param {string} key the identifier for the domain/range
*/
PlotAxis.prototype.chooseOption = function (key) {
var self = this;
this.options.forEach(function (option) {
if (option.key === key) {
self.active = option;
}
});
};
return PlotAxis;
}
);

View File

@ -1,78 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* Tracks the limit state of telemetry objects being plotted.
* @memberof platform/features/plot
* @constructor
* @param {platform/telemetry.TelemetryHandle} handle the handle
* to telemetry access
* @param {string} range the key to use when looking up range values
*/
function PlotLimitTracker(handle, range) {
this.handle = handle;
this.range = range;
this.legendClasses = {};
}
/**
* Update limit states to reflect the latest data.
*/
PlotLimitTracker.prototype.update = function () {
var legendClasses = {},
range = this.range,
handle = this.handle;
function updateLimit(telemetryObject) {
var limit = telemetryObject.getCapability('limit'),
datum = handle.getDatum(telemetryObject);
if (limit && datum) {
legendClasses[telemetryObject.getId()] =
(limit.evaluate(datum, range) || {}).cssClass;
}
}
handle.getTelemetryObjects().forEach(updateLimit);
this.legendClasses = legendClasses;
};
/**
* Get the CSS class associated with any limit violations for this
* telemetry object.
* @param {DomainObject} domainObject the telemetry object to check
* @returns {string} the CSS class name, if any
*/
PlotLimitTracker.prototype.getLegendClass = function (domainObject) {
var id = domainObject && domainObject.getId();
return id && this.legendClasses[id];
};
return PlotLimitTracker;
}
);

View File

@ -1,118 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
['./PlotSeriesWindow'],
function (PlotSeriesWindow) {
/**
* Represents a single line or trace of a plot.
* @param {{PlotLineBuffer}} buffer the plot buffer
* @constructor
*/
function PlotLine(buffer) {
this.buffer = buffer;
}
/**
* Add a point to this plot line.
* @param {number} domainValue the domain value
* @param {number} rangeValue the range value
*/
PlotLine.prototype.addPoint = function (domainValue, rangeValue) {
var buffer = this.buffer,
index;
// Make sure we got real/useful values here...
if (domainValue !== undefined && rangeValue !== undefined) {
index = buffer.findInsertionIndex(domainValue);
// Already in the buffer? Skip insertion
if (index < 0) {
return;
}
// Insert the point
if (!buffer.insertPoint(domainValue, rangeValue, index)) {
// If insertion failed, trim from the beginning...
buffer.trim(1);
// ...and try again.
buffer.insertPoint(domainValue, rangeValue, index);
}
}
};
/**
* Add a series of telemetry data to this plot line.
* @param {TelemetrySeries} series the data series
* @param {string} [domain] the key indicating which domain
* to use when looking up data from this series
* @param {string} [range] the key indicating which range
* to use when looking up data from this series
*/
PlotLine.prototype.addSeries = function (series, domain, range) {
var buffer = this.buffer;
// Insert a time-windowed data series into the buffer
function insertSeriesWindow(seriesWindow) {
var count = seriesWindow.getPointCount();
function doInsert() {
var firstTimestamp = seriesWindow.getDomainValue(0),
lastTimestamp = seriesWindow.getDomainValue(count - 1),
startIndex = buffer.findInsertionIndex(firstTimestamp),
endIndex = buffer.findInsertionIndex(lastTimestamp);
// Does the whole series fit in between two adjacent indexes?
if ((startIndex === endIndex) && startIndex > -1) {
// Insert it in between
buffer.insert(seriesWindow, startIndex);
} else {
// Split it up, and add the two halves
seriesWindow.split().forEach(insertSeriesWindow);
}
}
// Only insert if there are points to insert
if (count > 0) {
doInsert();
}
}
// Should try to add via insertion if a
// clear insertion point is available;
// if not, should split and add each half.
// Insertion operation also needs to factor out
// redundant timestamps, for overlapping data
insertSeriesWindow(new PlotSeriesWindow(
series,
domain,
range,
0,
series.getPointCount()
));
};
return PlotLine;
}
);

View File

@ -1,268 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* Contains the buffer used to draw a plot.
* @param {number} domainOffset number to subtract from domain values
* @param {number} initialSize initial buffer size
* @param {number} maxSize maximum buffer size
* @memberof platform/features/plot
* @constructor
*/
function PlotLineBuffer(domainOffset, initialSize, maxSize) {
this.buffer = new Float32Array(initialSize * 2);
this.rangeExtrema =
[Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
this.length = 0;
this.domainOffset = domainOffset;
this.initialSize = initialSize;
this.maxSize = maxSize;
}
// Binary search for an insertion index
PlotLineBuffer.prototype.binSearch = function (value, min, max) {
var mid = Math.floor((min + max) / 2),
found = this.buffer[mid * 2];
// On collisions, insert at same index
if (found === value) {
return mid;
}
// Otherwise, if we're down to a single index,
// we've found our insertion point
if (min >= max) {
// Compare the found timestamp with the search
// value to decide if we'll insert after or before.
return min + ((found < value) ? 1 : 0);
}
// Finally, do the recursive step
if (found < value) {
return this.binSearch(value, mid + 1, max);
} else {
return this.binSearch(value, min, mid - 1);
}
};
// Increase the size of the buffer
PlotLineBuffer.prototype.doubleBufferSize = function () {
var sz = Math.min(this.maxSize * 2, this.buffer.length * 2),
canDouble = sz > this.buffer.length,
doubled = canDouble && new Float32Array(sz);
if (canDouble) {
doubled.set(this.buffer); // Copy contents of original
this.buffer = doubled;
}
return canDouble;
};
// Decrease the size of the buffer
PlotLineBuffer.prototype.halveBufferSize = function () {
var sz = Math.max(this.initialSize * 2, this.buffer.length / 2),
canHalve = sz < this.buffer.length;
if (canHalve) {
this.buffer = new Float32Array(this.buffer.subarray(0, sz));
}
return canHalve;
};
// Set a value in the buffer
PlotLineBuffer.prototype.setValue = function (index, domainValue, rangeValue) {
this.buffer[index * 2] = domainValue - this.domainOffset;
this.buffer[index * 2 + 1] = rangeValue;
// Track min/max of range values (min/max for
// domain values can be read directly from buffer)
this.rangeExtrema[0] = Math.min(this.rangeExtrema[0], rangeValue);
this.rangeExtrema[1] = Math.max(this.rangeExtrema[1], rangeValue);
};
/**
* Get the WebGL-displayable buffer of points to plot.
* @returns {Float32Array} displayable buffer for this line
*/
PlotLineBuffer.prototype.getBuffer = function () {
return this.buffer;
};
/**
* Get the number of points stored in this buffer.
* @returns {number} the number of points stored
*/
PlotLineBuffer.prototype.getLength = function () {
return this.length;
};
/**
* Get the min/max range values that are currently in this
* buffer. Unlike range extrema, these will change as the
* buffer gets trimmed.
* @returns {number[]} min, max domain values
*/
PlotLineBuffer.prototype.getDomainExtrema = function () {
// Since these are ordered in the buffer, assume
// these are the values at the first and last index
return [
this.buffer[0] + this.domainOffset,
this.buffer[this.length * 2 - 2] + this.domainOffset
];
};
/**
* Get the min/max range values that have been observed for this
* buffer. Note that these values may have been trimmed out at
* some point.
* @returns {number[]} min, max range values
*/
PlotLineBuffer.prototype.getRangeExtrema = function () {
return this.rangeExtrema;
};
/**
* Remove values from this buffer.
* Normally, values are removed from the start
* of the buffer; a truthy value in the second argument
* will cause values to be removed from the end.
* @param {number} count number of values to remove
* @param {boolean} [fromEnd] true if the most recent
* values should be removed
*/
PlotLineBuffer.prototype.trim = function (count, fromEnd) {
// If we're removing values from the start...
if (!fromEnd) {
// ...do so by shifting buffer contents over
this.buffer.set(this.buffer.subarray(2 * count));
}
// Reduce used buffer size accordingly
this.length -= count;
// Finally, if less than half of the buffer is being
// used, free up some memory.
if (this.length < this.buffer.length / 4) {
this.halveBufferSize();
}
};
/**
* Insert data from the provided series at the specified
* index. If this would exceed the buffer's maximum capacity,
* this operation fails and the buffer is unchanged.
* @param {TelemetrySeries} series the series to insert
* @param {number} index the index at which to insert this
* series
* @returns {boolean} true if insertion succeeded; otherwise
* false
*/
PlotLineBuffer.prototype.insert = function (series, index) {
var sz = series.getPointCount(),
i;
// Don't allow append after the end; that doesn't make sense
index = Math.min(index, this.length);
// Resize if necessary
while (sz > ((this.buffer.length / 2) - this.length)) {
if (!this.doubleBufferSize()) {
// Can't make room for this, insertion fails
return false;
}
}
// Shift data over if necessary
if (index < this.length) {
this.buffer.set(
this.buffer.subarray(index * 2, this.length * 2),
(index + sz) * 2
);
}
// Insert data into the set
for (i = 0; i < sz; i += 1) {
this.setValue(
i + index,
series.getDomainValue(i),
series.getRangeValue(i)
);
}
// Increase the length
this.length += sz;
// Indicate that insertion was successful
return true;
};
/**
* Append a single data point.
* @memberof platform/features/plot.PlotLineBuffer#
*/
PlotLineBuffer.prototype.insertPoint = function (domainValue, rangeValue) {
// Ensure there is space for this point
if (this.length >= (this.buffer.length / 2)) {
if (!this.doubleBufferSize()) {
return false;
}
}
// Put the data in the buffer
this.setValue(this.length, domainValue, rangeValue);
// Update length
this.length += 1;
// Indicate that this was successful
return true;
};
/**
* Find an index for inserting data with this
* timestamp. The second argument indicates whether
* we are searching for insert-before or insert-after
* positions.
* Timestamps are meant to be unique, so if a collision
* occurs, this will return -1.
* @param {number} timestamp timestamp to insert
* @returns {number} the index for insertion (or -1)
*/
PlotLineBuffer.prototype.findInsertionIndex = function (timestamp) {
var value = timestamp - this.domainOffset;
// Handle empty buffer case and check for an
// append opportunity (which is most common case for
// real-time data so is optimized-for) before falling
// back to a binary search for the insertion point.
return (this.length < 1) ? 0 :
(value > this.buffer[this.length * 2 - 2]) ? this.length :
this.binSearch(value, 0, this.length - 1);
};
return PlotLineBuffer;
}
);

View File

@ -1,133 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* Plot palette. Defines colors for various plot lines.
*/
define(
function () {
// Prepare different forms of the palette, since we wish to
// describe colors in several ways (as RGB 0-255, as
// RGB 0.0-1.0, or as stylesheet-appropriate #-prefixed colors).
var integerPalette = [
[0x20, 0xB2, 0xAA],
[0x9A, 0xCD, 0x32],
[0xFF, 0x8C, 0x00],
[0xD2, 0xB4, 0x8C],
[0x40, 0xE0, 0xD0],
[0x41, 0x69, 0xFF],
[0xFF, 0xD7, 0x00],
[0x6A, 0x5A, 0xCD],
[0xEE, 0x82, 0xEE],
[0xCC, 0x99, 0x66],
[0x99, 0xCC, 0xCC],
[0x66, 0xCC, 0x33],
[0xFF, 0xCC, 0x00],
[0xFF, 0x66, 0x33],
[0xCC, 0x66, 0xFF],
[0xFF, 0x00, 0x66],
[0xFF, 0xFF, 0x00],
[0x80, 0x00, 0x80],
[0x00, 0x86, 0x8B],
[0x00, 0x8A, 0x00],
[0xFF, 0x00, 0x00],
[0x00, 0x00, 0xFF],
[0xF5, 0xDE, 0xB3],
[0xBC, 0x8F, 0x8F],
[0x46, 0x82, 0xB4],
[0xFF, 0xAF, 0xAF],
[0x43, 0xCD, 0x80],
[0xCD, 0xC1, 0xC5],
[0xA0, 0x52, 0x2D],
[0x64, 0x95, 0xED]
], stringPalette = integerPalette.map(function (arr) {
// Convert to # notation for use in styles
return '#' + arr.map(function (c) {
return (c < 16 ? '0' : '') + c.toString(16);
}).join('');
}), floatPalette = integerPalette.map(function (arr) {
return arr.map(function (c) {
return c / 255.0;
}).concat([1]); // RGBA
});
/**
* PlotPalette allows a consistent set of colors to be retrieved
* by index, in various color formats. All PlotPalette methods are
* static, so there is no need for a constructor call; using
* this will simply return PlotPalette itself.
* @memberof platform/features/plot
* @constructor
*/
function PlotPalette() {
return PlotPalette;
}
/**
* Look up a color in the plot's palette, by index.
* This will be returned as a three element array of RGB
* values, as integers in the range of 0-255.
* @param {number} i the index of the color to look up
* @return {number[]} the color, as integer RGB values
*/
PlotPalette.getIntegerColor = function (i) {
return integerPalette[Math.floor(i) % integerPalette.length];
};
/**
* Look up a color in the plot's palette, by index.
* This will be returned as a three element array of RGB
* values, in the range of 0.0-1.0.
*
* This format is present specifically to support use with
* WebGL, which expects colors of that form.
*
* @param {number} i the index of the color to look up
* @return {number[]} the color, as floating-point RGB values
*/
PlotPalette.getFloatColor = function (i) {
return floatPalette[Math.floor(i) % floatPalette.length];
};
/**
* Look up a color in the plot's palette, by index.
* This will be returned as a string using #-prefixed
* six-digit RGB hex notation (e.g. #FF0000)
* See http://www.w3.org/TR/css3-color/#rgb-color.
*
* This format is useful for representing colors in in-line
* styles.
*
* @param {number} i the index of the color to look up
* @return {string} the color, as a style-friendly string
*/
PlotPalette.getStringColor = function (i) {
return stringPalette[Math.floor(i) % stringPalette.length];
};
return PlotPalette;
}
);

View File

@ -1,141 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* The PlotPanZoomStack is responsible for maintaining the
* pan-zoom state of a plot (expressed as a boundary starting
* at an origin and extending to certain dimensions) in a
* stack, to support the back and unzoom buttons in plot controls.
*
* Dimensions and origins are here described each by two-element
* arrays, where the first element describes a value or quantity
* along the domain axis, and the second element describes the same
* along the range axis.
*
* @memberof platform/features/plot
* @constructor
* @param {number[]} origin the plot's origin, initially
* @param {number[]} dimensions the plot's dimensions, initially
*/
function PlotPanZoomStack(origin, dimensions) {
// Use constructor parameters as the stack's initial state
this.stack = [{ origin: origin, dimensions: dimensions }];
}
// Various functions which follow are simply wrappers for
// normal stack-like array methods, with the exception that
// they prevent undesired modification and enforce that this
// stack must remain non-empty.
// See JSDoc for specific methods below for more detail.
/**
* Get the current stack depth; that is, the number
* of items on the stack. A depth of one means that no
* panning or zooming relative to the base value has
* been applied.
* @returns {number} the depth of the stack
*/
PlotPanZoomStack.prototype.getDepth = function getDepth() {
return this.stack.length;
};
/**
* Push a new pan-zoom state onto the stack; this will
* become the active pan-zoom state.
* @param {number[]} origin the new origin
* @param {number[]} dimensions the new dimensions
*/
PlotPanZoomStack.prototype.pushPanZoom = function (origin, dimensions) {
this.stack.push({ origin: origin, dimensions: dimensions });
};
/**
* Pop a pan-zoom state from the stack. Whatever pan-zoom
* state was previously present will become current.
* If called when there is only one pan-zoom state on the
* stack, this acts as a no-op (that is, the lowest
* pan-zoom state on the stack cannot be popped, to ensure
* that some pan-zoom state is always available.)
*/
PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() {
if (this.stack.length > 1) {
this.stack.pop();
}
};
/**
* Set the base pan-zoom state; that is, the state at the
* bottom of the stack. This allows the "unzoomed" state of
* a plot to be updated (e.g. as new data comes in) without
* interfering with the user's chosen zoom level.
* @param {number[]} origin the base origin
* @param {number[]} dimensions the base dimensions
* @memberof platform/features/plot.PlotPanZoomStack#
*/
PlotPanZoomStack.prototype.setBasePanZoom = function (origin, dimensions) {
this.stack[0] = { origin: origin, dimensions: dimensions };
};
/**
* Clear the pan-zoom stack down to its bottom element;
* in effect, pop all elements but the last, e.g. to remove
* any temporary user modifications to pan-zoom state.
*/
PlotPanZoomStack.prototype.clearPanZoom = function clearPanZoom() {
this.stack = [this.stack[0]];
};
/**
* Get the current pan-zoom state (the state at the top
* of the stack), expressed as an object with "origin" and
* "dimensions" fields.
* @returns {object} the current pan-zoom state
*/
PlotPanZoomStack.prototype.getPanZoom = function getPanZoom() {
return this.stack[this.stack.length - 1];
};
/**
* Get the current origin, as represented on the top of the
* stack.
* @returns {number[]} the current plot origin
*/
PlotPanZoomStack.prototype.getOrigin = function getOrigin() {
return this.getPanZoom().origin;
};
/**
* Get the current dimensions, as represented on the top of
* the stack.
* @returns {number[]} the current plot dimensions
*/
PlotPanZoomStack.prototype.getDimensions = function getDimensions() {
return this.getPanZoom().dimensions;
};
return PlotPanZoomStack;
}
);

View File

@ -1,167 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
['./PlotPanZoomStack'],
function (PlotPanZoomStack) {
/**
* A plot pan zoom stack group provides a collection of individual
* pan-zoom stacks that synchronize upon the domain axis, but
* remain independent upon the range axis. This supports panning
* and zooming in stacked-plot mode (and, importantly,
* stepping back through those states.)
* @memberof platform/features/plot
* @constructor
* @param {number} count the number of stacks to include in this
* group
*/
function PlotPanZoomStackGroup(count) {
var self = this;
// Push a pan-zoom state; the index argument identifies
// which stack originated the request (all other stacks
// will ignore the range part of the change.)
function pushPanZoom(origin, dimensions, index) {
self.stacks.forEach(function (stack, i) {
if (i === index) {
// Do a normal push for the specified stack
stack.pushPanZoom(origin, dimensions);
} else {
// For other stacks, do a push, but repeat
// their current range axis bounds.
stack.pushPanZoom(
[origin[0], stack.getOrigin()[1]],
[dimensions[0], stack.getDimensions()[1]]
);
}
});
}
// Decorate a pan-zoom stack; returns an object with
// the same interface, but whose stack-mutation methods
// effect all items in the group.
function decorateStack(stack, index) {
var result = Object.create(stack);
// Use the methods defined above
result.pushPanZoom = function (origin, dimensions) {
pushPanZoom(origin, dimensions, index);
};
result.setBasePanZoom = function () {
self.setBasePanZoom.apply(self, arguments);
};
result.popPanZoom = function () {
self.popPanZoom.apply(self, arguments);
};
result.clearPanZoom = function () {
self.clearPanZoom.apply(self, arguments);
};
return result;
}
// Create the stacks in this group ...
this.stacks = [];
while (this.stacks.length < count) {
this.stacks.push(new PlotPanZoomStack([], []));
}
// ... and their decorated-to-synchronize versions.
this.decoratedStacks = this.stacks.map(decorateStack);
}
/**
* Pop a pan-zoom state from all stacks in the group.
* If called when there is only one pan-zoom state on each
* stack, this acts as a no-op (that is, the lowest
* pan-zoom state on the stack cannot be popped, to ensure
* that some pan-zoom state is always available.)
*/
PlotPanZoomStackGroup.prototype.popPanZoom = function () {
this.stacks.forEach(function (stack) {
stack.popPanZoom();
});
};
/**
* Set the base pan-zoom state for all stacks in this group.
* This changes the state at the bottom of each stack.
* This allows the "unzoomed" state of plots to be updated
* (e.g. as new data comes in) without
* interfering with the user's chosen pan/zoom states.
* @param {number[]} origin the base origin
* @param {number[]} dimensions the base dimensions
*/
PlotPanZoomStackGroup.prototype.setBasePanZoom = function (origin, dimensions) {
this.stacks.forEach(function (stack) {
stack.setBasePanZoom(origin, dimensions);
});
};
/**
* Clear all pan-zoom stacks in this group down to
* their bottom element; in effect, pop all elements
* but the last, e.g. to remove any temporary user
* modifications to pan-zoom state.
*/
PlotPanZoomStackGroup.prototype.clearPanZoom = function () {
this.stacks.forEach(function (stack) {
stack.clearPanZoom();
});
};
/**
* Get the current stack depth; that is, the number
* of items on each stack in the group.
* A depth of one means that no
* panning or zooming relative to the base value has
* been applied.
* @returns {number} the depth of the stacks in this group
*/
PlotPanZoomStackGroup.prototype.getDepth = function () {
// All stacks are kept in sync, so look up depth
// from the first one.
return this.stacks.length > 0 ? this.stacks[0].getDepth() : 0;
};
/**
* Get a specific pan-zoom stack in this group.
* Stacks are specified by index; this index must be less
* than the count provided at construction time, and must
* not be less than zero.
* The stack returned by this function will be synchronized
* to other stacks in this group; that is, mutating that
* stack directly will result in other stacks in this group
* undergoing similar updates to ensure that domain bounds
* remain the same.
* @param {number} index the index of the stack to get
* @returns {PlotPanZoomStack} the pan-zoom stack in the
* group identified by that index
*/
PlotPanZoomStackGroup.prototype.getPanZoomStack = function (index) {
return this.decoratedStacks[index];
};
return PlotPanZoomStackGroup;
}
);

View File

@ -1,95 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* A PlotPosition converts from pixel coordinates to domain-range
* coordinates, based on the current plot boundary as described on
* the pan-zoom stack.
*
* These coordinates are not updated after construction; that is,
* they represent the result of the conversion at the time the
* PlotPosition was instantiated. Care should be taken when retaining
* PlotPosition objects across changes to the pan-zoom stack.
*
* @memberof platform/features/plot
* @constructor
* @param {number} x the horizontal pixel position in the plot area
* @param {number} y the vertical pixel position in the plot area
* @param {number} width the width of the plot area
* @param {number} height the height of the plot area
* @param {PanZoomStack} panZoomStack the applicable pan-zoom stack,
* used to determine the plot's domain-range boundaries.
*/
function PlotPosition(x, y, width, height, panZoomStack) {
var panZoom = panZoomStack.getPanZoom(),
origin = panZoom.origin,
dimensions = panZoom.dimensions;
function convert(v, i) {
return v * dimensions[i] + origin[i];
}
if (!dimensions || !origin) {
// We need both dimensions and origin to compute a position
this.position = [];
} else {
// Convert from pixel to domain-range space.
// Note that range is reversed from the y-axis in pixel space
//(positive range points up, positive pixel-y points down)
this.position =
[x / width, (height - y) / height].map(convert);
}
}
/**
* Get the domain value corresponding to this pixel position.
* @returns {number} the domain value
*/
PlotPosition.prototype.getDomain = function () {
return this.position[0];
};
/**
* Get the range value corresponding to this pixel position.
* @returns {number} the range value
*/
PlotPosition.prototype.getRange = function () {
return this.position[1];
};
/**
* Get the domain and values corresponding to this
* pixel position.
* @returns {number[]} an array containing the domain and
* the range value, in that order
*/
PlotPosition.prototype.getPosition = function () {
return this.position;
};
return PlotPosition;
}
);

View File

@ -1,153 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* Prepares data to be rendered in a GL Plot. Handles
* the conversion from data API to displayable buffers.
*/
define(
function () {
function identity(x) {
return x;
}
/**
* The PlotPreparer is responsible for handling data sets and
* preparing them to be rendered. It creates a WebGL-plottable
* Float32Array for each trace, and tracks the boundaries of the
* data sets (since this is convenient to do during the same pass).
* @memberof platform/features/plot
* @constructor
* @param {Telemetry[]} datas telemetry data objects
* @param {string} domain the key to use when looking up domain values
* @param {string} range the key to use when looking up range values
*/
function PlotPreparer(datas, domain, range) {
var index,
vertices = [],
max = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
x,
y,
domainOffset = Number.POSITIVE_INFINITY;
// Remove any undefined data sets
datas = (datas || []).filter(identity);
// Do a first pass to determine the domain offset.
// This will be use to reduce the magnitude of domain values
// in the buffer, to minimize loss-of-precision when
// converting to a 32-bit float.
datas.forEach(function (data) {
domainOffset = Math.min(data.getDomainValue(0, domain), domainOffset);
});
// Assemble buffers, and track bounds of the data present
datas.forEach(function (data, i) {
vertices.push([]);
for (index = 0; index < data.getPointCount(); index += 1) {
x = data.getDomainValue(index, domain);
y = data.getRangeValue(index, range);
vertices[i].push(x - domainOffset);
vertices[i].push(y);
min[0] = Math.min(min[0], x);
min[1] = Math.min(min[1], y);
max[0] = Math.max(max[0], x);
max[1] = Math.max(max[1], y);
}
});
// If range is empty, add some padding
if (max[1] === min[1]) {
max[1] = max[1] + 1.0;
min[1] = min[1] - 1.0;
}
// Convert to Float32Array
this.buffers = vertices.map(function (v) {
return new Float32Array(v);
});
this.min = min;
this.max = max;
this.domainOffset = domainOffset;
}
/**
* Get the dimensions which bound all data in the provided
* data sets. This is given as a two-element array where the
* first element is domain, and second is range.
* @returns {number[]} the dimensions which bound this data set
*/
PlotPreparer.prototype.getDimensions = function () {
var max = this.max, min = this.min;
return [max[0] - min[0], max[1] - min[1]];
};
/**
* Get the origin of this data set's boundary.
* This is given as a two-element array where the
* first element is domain, and second is range.
* The domain value here is not adjusted by the domain offset.
* @returns {number[]} the origin of this data set's boundary
*/
PlotPreparer.prototype.getOrigin = function () {
return this.min;
};
/**
* Get the domain offset; this offset will have been subtracted
* from all domain values in all buffers returned by this
* preparer, in order to minimize loss-of-precision due to
* conversion to the 32-bit float format needed by WebGL.
* @returns {number} the domain offset
*/
PlotPreparer.prototype.getDomainOffset = function () {
return this.domainOffset;
};
/**
* Get all renderable buffers for this data set. This will
* be returned as an array which can be correlated back to
* the provided telemetry data objects (from the constructor
* call) by index.
*
* Internally, these are flattened; each buffer contains a
* sequence of alternating domain and range values.
*
* All domain values in all buffers will have been adjusted
* from their original values by subtraction of the domain
* offset; this minimizes loss-of-precision resulting from
* the conversion to 32-bit floats, which may otherwise
* cause aliasing artifacts (particularly for timestamps)
*
* @returns {Float32Array[]} the buffers for these traces
*/
PlotPreparer.prototype.getBuffers = function () {
return this.buffers;
};
return PlotPreparer;
}
);

View File

@ -1,80 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* Provides a window on a telemetry data series, to support
* insertion into a plot line.
* @constructor
* @memberof platform/features/plot
* @implements {TelemetrySeries}
*/
function PlotSeriesWindow(series, domain, range, start, end) {
this.series = series;
this.domain = domain;
this.range = range;
this.start = start;
this.end = end;
}
PlotSeriesWindow.prototype.getPointCount = function () {
return this.end - this.start;
};
PlotSeriesWindow.prototype.getDomainValue = function (index) {
return this.series.getDomainValue(index + this.start, this.domain);
};
PlotSeriesWindow.prototype.getRangeValue = function (index) {
return this.series.getRangeValue(index + this.start, this.range);
};
/**
* Split this series into two series of equal (or nearly-equal) size.
* @returns {PlotSeriesWindow[]} two series
*/
PlotSeriesWindow.prototype.split = function () {
var mid = Math.floor((this.end + this.start) / 2);
return ((this.end - this.start) > 1) ?
[
new PlotSeriesWindow(
this.series,
this.domain,
this.range,
this.start,
mid
),
new PlotSeriesWindow(
this.series,
this.domain,
this.range,
mid,
this.end
)
] : [];
};
return PlotSeriesWindow;
}
);

View File

@ -1,76 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
var DIGITS = 3;
/**
* Wraps a `TelemetryFormatter` to provide formats for domain and
* range values; provides a single place to track domain/range
* formats within a plot, allowing other plot elements to simply
* request that values be formatted.
* @constructor
* @memberof platform/features/plot
* @implements {platform/telemetry.TelemetryFormatter}
* @param {TelemetryFormatter} telemetryFormatter the formatter
* to wrap.
*/
function PlotTelemetryFormatter(telemetryFormatter) {
this.telemetryFormatter = telemetryFormatter;
}
/**
* Specify the format to use for domain values.
* @param {string} key the format's identifier
*/
PlotTelemetryFormatter.prototype.setDomainFormat = function (key) {
this.domainFormat = key;
};
/**
* Specify the format to use for range values.
* @param {string} key the format's identifier
*/
PlotTelemetryFormatter.prototype.setRangeFormat = function (key) {
this.rangeFormat = key;
};
PlotTelemetryFormatter.prototype.formatDomainValue = function (value) {
return this.telemetryFormatter
.formatDomainValue(value, this.domainFormat);
};
PlotTelemetryFormatter.prototype.formatRangeValue = function (value) {
if (typeof value === 'number') {
return value.toFixed(DIGITS);
}
return this.telemetryFormatter
.formatRangeValue(value, this.rangeFormat);
};
return PlotTelemetryFormatter;
}
);

View File

@ -1,102 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
[],
function () {
/**
* The PlotTickGenerator provides labels for ticks along the
* domain and range axes of the plot, to support the plot
* template.
*
* @memberof platform/features/plot
* @constructor
* @param {PlotPanZoomStack} panZoomStack the pan-zoom stack for
* this plot, used to determine plot boundaries
* @param {TelemetryFormatter} formatter used to format (for display)
* domain and range values.
*/
function PlotTickGenerator(panZoomStack, formatter) {
this.panZoomStack = panZoomStack;
this.formatter = formatter;
}
// For phantomjs compatibility, for headless testing
// (Function.prototype.bind unsupported)
function bind(fn, thisObj) {
return fn.bind ? fn.bind(thisObj) : function () {
return fn.apply(thisObj, arguments);
};
}
// Generate ticks; interpolate from start up to
// start + span in count steps, using the provided
// formatter to represent each value.
PlotTickGenerator.prototype.generateTicks = function (start, span, count, format) {
var step = span / (count - 1),
result = [],
i;
for (i = 0; i < count; i += 1) {
result.push({
//If data to show, display label for each tick line, otherwise show lines but suppress labels.
label: span > 0 ? format(i * step + start) : ''
});
}
return result;
};
/**
* Generate tick marks for the domain axis.
* @param {number} count the number of ticks
* @returns {string[]} labels for those ticks
*/
PlotTickGenerator.prototype.generateDomainTicks = function (count) {
var panZoom = this.panZoomStack.getPanZoom();
return this.generateTicks(
panZoom.origin[0],
panZoom.dimensions[0],
count,
bind(this.formatter.formatDomainValue, this.formatter)
);
};
/**
* Generate tick marks for the range axis.
* @param {number} count the number of ticks
* @returns {string[]} labels for those ticks
*/
PlotTickGenerator.prototype.generateRangeTicks = function (count) {
var panZoom = this.panZoomStack.getPanZoom();
return this.generateTicks(
panZoom.origin[1],
panZoom.dimensions[1],
count,
bind(this.formatter.formatRangeValue, this.formatter)
);
};
return PlotTickGenerator;
}
);

View File

@ -1,353 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
['./PlotLine', './PlotLineBuffer'],
function (PlotLine, PlotLineBuffer) {
var MAX_POINTS = 86400,
PADDING_RATIO = 0.10, // Padding percentage for top & bottom
INITIAL_SIZE = 675; // 1/128 of MAX_POINTS
/**
* The PlotPreparer is responsible for handling data sets and
* preparing them to be rendered. It creates a WebGL-plottable
* Float32Array for each trace, and tracks the boundaries of the
* data sets (since this is convenient to do during the same pass).
* @memberof platform/features/plot
* @constructor
* @param {TelemetryHandle} handle the handle to telemetry access
* @param {string} domain the key to use when looking up domain values
* @param {string} range the key to use when looking up range values
* @param {number} fixedDuration maximum plot duration to display
* @param {number} maxPoints maximum number of points to display
*/
function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
this.handle = handle;
this.domain = domain;
this.range = range;
this.fixedDuration = fixedDuration;
this.maxPoints = maxPoints;
this.ids = [];
this.lines = {};
this.buffers = {};
this.bufferArray = [];
// Use a default MAX_POINTS if none is provided
this.maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
this.dimensions = [0, 0];
this.origin = [0, 0];
// Initially prepare state for these objects.
// Note that this may be an empty array at this time,
// so we also need to check during update cycles.
this.update();
}
// Look up a domain object's id (for mapping, below)
function getId(domainObject) {
return domainObject.getId();
}
// Used in the reduce step of updateExtrema
function reduceExtrema(a, b) {
return [Math.min(a[0], b[0]), Math.max(a[1], b[1])];
}
// Convert a domain/range extrema to plot dimensions
function dimensionsOf(extrema) {
return extrema[1] - extrema[0];
}
// Convert a domain/range extrema to a plot origin
function originOf(extrema) {
return extrema[0];
}
// Check if this set of ids matches the current set of ids
// (used to detect if line preparation can be skipped)
PlotUpdater.prototype.idsMatch = function (nextIds) {
var ids = this.ids;
return ids.length === nextIds.length &&
nextIds.every(function (id, index) {
return ids[index] === id;
});
};
// Prepare plot lines for this group of telemetry objects
PlotUpdater.prototype.prepareLines = function (telemetryObjects) {
var nextIds = telemetryObjects.map(getId),
next = {},
self = this;
// Detect if we already have everything we need prepared
if (this.idsMatch(nextIds)) {
// Nothing to prepare, move on
return;
}
// Built up a set of ids. Note that we can only
// create plot lines after our domain offset has
// been determined.
if (this.domainOffset !== undefined) {
// Update list of ids in use
this.ids = nextIds;
// Create buffers for these objects
this.bufferArray = this.ids.map(function (id) {
self.buffers[id] = self.buffers[id] || new PlotLineBuffer(
self.domainOffset,
INITIAL_SIZE,
self.maxPoints
);
next[id] =
self.lines[id] || new PlotLine(self.buffers[id]);
return self.buffers[id];
});
}
// If there are no more lines, clear the domain offset
if (Object.keys(next).length < 1) {
this.domainOffset = undefined;
}
// Update to the current set of lines
this.lines = next;
};
// Initialize the domain offset, based on these observed values
PlotUpdater.prototype.initializeDomainOffset = function (values) {
this.domainOffset =
((this.domainOffset === undefined) && (values.length > 0)) ?
(values.reduce(function (a, b) {
return (a || 0) + (b || 0);
}, 0) / values.length) :
this.domainOffset;
};
// Expand range slightly so points near edges are visible
PlotUpdater.prototype.expandRange = function () {
var padding = PADDING_RATIO * this.dimensions[1],
top;
padding = Math.max(padding, 1.0);
top = Math.ceil(this.origin[1] + this.dimensions[1] + padding / 2);
this.origin[1] = Math.floor(this.origin[1] - padding / 2);
this.dimensions[1] = top - this.origin[1];
};
// Update dimensions and origin based on extrema of plots
PlotUpdater.prototype.updateBounds = function () {
var bufferArray = this.bufferArray.filter(function (lineBuffer) {
return lineBuffer.getLength() > 0; // Ignore empty lines
}),
priorDomainOrigin = this.origin[0],
priorDomainDimensions = this.dimensions[0];
if (bufferArray.length > 0) {
this.domainExtrema = bufferArray.map(function (lineBuffer) {
return lineBuffer.getDomainExtrema();
}).reduce(reduceExtrema);
this.rangeExtrema = bufferArray.map(function (lineBuffer) {
return lineBuffer.getRangeExtrema();
}).reduce(reduceExtrema);
// Calculate best-fit dimensions
this.dimensions = [this.domainExtrema, this.rangeExtrema]
.map(dimensionsOf);
this.origin = [this.domainExtrema, this.rangeExtrema]
.map(originOf);
// Enforce some minimum visible area
this.expandRange();
// Suppress domain changes when pinned
if (this.hasSpecificDomainBounds) {
this.origin[0] = priorDomainOrigin;
this.dimensions[0] = priorDomainDimensions;
if (this.following) {
this.origin[0] = Math.max(
this.domainExtrema[1] - this.dimensions[0],
this.origin[0]
);
}
}
// ...then enforce a fixed duration if needed
if (this.fixedDuration !== undefined) {
this.origin[0] = this.origin[0] + this.dimensions[0] -
this.fixedDuration;
this.dimensions[0] = this.fixedDuration;
}
}
};
// Add latest data for this domain object
PlotUpdater.prototype.addPointFor = function (domainObject) {
var line = this.lines[domainObject.getId()];
if (line) {
line.addPoint(
this.handle.getDomainValue(domainObject, this.domain),
this.handle.getRangeValue(domainObject, this.range)
);
}
};
/**
* Update with latest data.
*/
PlotUpdater.prototype.update = function update() {
var objects = this.handle.getTelemetryObjects(),
self = this;
// Initialize domain offset if necessary
if (this.domainOffset === undefined) {
this.initializeDomainOffset(objects.map(function (obj) {
return self.handle.getDomainValue(obj, self.domain);
}).filter(function (value) {
return typeof value === 'number';
}));
}
// Make sure lines are available
this.prepareLines(objects);
// Add new data
objects.forEach(function (domainObject, index) {
self.addPointFor(domainObject, index);
});
// Then, update extrema
this.updateBounds();
};
/**
* Get the dimensions which bound all data in the provided
* data sets. This is given as a two-element array where the
* first element is domain, and second is range.
* @returns {number[]} the dimensions which bound this data set
*/
PlotUpdater.prototype.getDimensions = function () {
return this.dimensions;
};
/**
* Get the origin of this data set's boundary.
* This is given as a two-element array where the
* first element is domain, and second is range.
* The domain value here is not adjusted by the domain offset.
* @returns {number[]} the origin of this data set's boundary
*/
PlotUpdater.prototype.getOrigin = function () {
return this.origin;
};
/**
* Get the domain offset; this offset will have been subtracted
* from all domain values in all buffers returned by this
* preparer, in order to minimize loss-of-precision due to
* conversion to the 32-bit float format needed by WebGL.
* @returns {number} the domain offset
* @memberof platform/features/plot.PlotUpdater#
*/
PlotUpdater.prototype.getDomainOffset = function () {
return this.domainOffset;
};
/**
* Get all renderable buffers for this data set. This will
* be returned as an array which can be correlated back to
* the provided telemetry data objects (from the constructor
* call) by index.
*
* Internally, these are flattened; each buffer contains a
* sequence of alternating domain and range values.
*
* All domain values in all buffers will have been adjusted
* from their original values by subtraction of the domain
* offset; this minimizes loss-of-precision resulting from
* the conversion to 32-bit floats, which may otherwise
* cause aliasing artifacts (particularly for timestamps)
*
* @returns {Float32Array[]} the buffers for these traces
* @memberof platform/features/plot.PlotUpdater#
*/
PlotUpdater.prototype.getLineBuffers = function () {
return this.bufferArray;
};
/**
* Set the start and end boundaries (usually time) for the
* domain axis of this updater.
*/
PlotUpdater.prototype.setDomainBounds = function (start, end) {
this.fixedDuration = end - start;
this.origin[0] = start;
this.dimensions[0] = this.fixedDuration;
// Suppress follow behavior if we have windowed in on the past
this.hasSpecificDomainBounds = true;
this.following =
!this.domainExtrema || (end >= this.domainExtrema[1]);
};
/**
* Fill in historical data.
*/
PlotUpdater.prototype.addHistorical = function (domainObject, series) {
var count = series ? series.getPointCount() : 0,
line;
// Nothing to do if it's an empty series
if (count < 1) {
return;
}
// Initialize domain offset if necessary
if (this.domainOffset === undefined) {
this.initializeDomainOffset([
series.getDomainValue(0, this.domain),
series.getDomainValue(count - 1, this.domain)
]);
}
// Make sure lines are available
this.prepareLines(this.handle.getTelemetryObjects());
// Look up the line for this domain object
line = this.lines[domainObject.getId()];
// ...and put the data into it.
if (line) {
line.addSeries(series, this.domain, this.range);
}
// Update extrema
this.updateBounds();
};
return PlotUpdater;
}
);

View File

@ -1,155 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["./PlotOverlayMode", "./PlotStackMode"],
function (PlotOverlayMode, PlotStackMode) {
var STACKED = {
key: "stacked",
name: "Stacked",
cssClass: "icon-plot-stacked",
Constructor: PlotStackMode
},
OVERLAID = {
key: "overlaid",
name: "Overlaid",
cssClass: "icon-plot-overlay",
Constructor: PlotOverlayMode
};
/**
* Handles distinct behavior associated with different
* plot modes.
*
* @interface platform/features/plot.PlotModeHandler
* @private
*/
/**
* Plot telemetry to the sub-plot(s) managed by this mode.
* @param {platform/features/plot.PlotUpdater} updater a source
* of data that is ready to plot
* @method platform/features/plot.PlotModeHandler#plotTelemetry
*/
/**
* Get all sub-plots to be displayed in this mode; used
* to populate the plot template.
* @return {platform/features/plot.SubPlot[]} all sub-plots to
* display in this mode
* @method platform/features/plot.PlotModeHandler#getSubPlots
*/
/**
* Check if we are not in our base pan-zoom state (that is,
* there are some temporary user modifications to the
* current pan-zoom state.)
* @returns {boolean} true if not in the base pan-zoom state
* @method platform/features/plot.PlotModeHandler#isZoomed
*/
/**
* Undo the most recent pan/zoom change and restore
* the prior state.
* @method platform/features/plot.PlotModeHandler#stepBackPanZoom
*/
/**
* Undo all pan/zoom change and restore the base state.
* @method platform/features/plot.PlotModeHandler#unzoom
*/
/**
* Determines which plotting modes (stacked/overlaid)
* are applicable in a given plot view, maintains current
* selection state thereof, and provides handlers for the
* different behaviors associated with these modes.
* @memberof platform/features/plot
* @constructor
* @param {DomainObject[]} telemetryObjects the telemetry objects being
* represented in this plot view
* @param {platform/features/plot.SubPlotFactory} subPlotFactory a
* factory for creating sub-plots
*/
function PlotModeOptions(telemetryObjects, subPlotFactory) {
this.options = telemetryObjects.length > 1 ?
[OVERLAID, STACKED] : [OVERLAID];
this.mode = this.options[0]; // Initial selection (overlaid)
this.telemetryObjects = telemetryObjects;
this.subPlotFactory = subPlotFactory;
}
/**
* Get a handler for the current mode. This will handle
* plotting telemetry, providing subplots for the template,
* and view-level interactions with pan-zoom state.
* @returns {PlotOverlayMode|PlotStackMode} a handler
* for the current mode
*/
PlotModeOptions.prototype.getModeHandler = function () {
// Lazily initialize
if (!this.modeHandler) {
this.modeHandler = new this.mode.Constructor(
this.telemetryObjects,
this.subPlotFactory
);
}
return this.modeHandler;
};
/**
* Get all mode options available for each plot. Each
* mode contains a `name` and `cssClass` field suitable
* for display in a template.
* @return {Array} the available modes
*/
PlotModeOptions.prototype.getModeOptions = function () {
return this.options;
};
/**
* Get the plotting mode option currently in use.
* This will be one of the elements returned from
* `getModeOptions`.
* @return {*} the current mode
*/
PlotModeOptions.prototype.getMode = function () {
return this.mode;
};
/**
* Set the plotting mode option to use.
* The passed argument must be one of the options
* returned by `getModeOptions`.
* @param {object} option one of the plot mode options
* from `getModeOptions`
*/
PlotModeOptions.prototype.setMode = function (option) {
if (this.mode !== option) {
this.mode = option;
// Clear the existing mode handler, so it
// can be instantiated next time it's needed.
this.modeHandler = undefined;
}
};
return PlotModeOptions;
}
);

View File

@ -1,88 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../SubPlot", "../elements/PlotPalette", "../elements/PlotPanZoomStack"],
function (SubPlot, PlotPalette, PlotPanZoomStack) {
/**
* Handles plotting in Overlaid mode. In overlaid mode, there
* is one sub-plot which contains all plotted objects.
* @memberof platform/features/plot
* @constructor
* @implements {platform/features/plot.PlotModeHandler}
* @param {DomainObject[]} the domain objects to be plotted
*/
function PlotOverlayMode(telemetryObjects, subPlotFactory) {
this.panZoomStack = new PlotPanZoomStack([], []);
this.subplot = subPlotFactory.createSubPlot(
telemetryObjects,
this.panZoomStack
);
this.subplots = [this.subplot];
}
PlotOverlayMode.prototype.plotTelemetry = function (updater) {
// Fit to the boundaries of the data, but don't
// override any user-initiated pan-zoom changes.
this.panZoomStack.setBasePanZoom(
updater.getOrigin(),
updater.getDimensions()
);
// Track the domain offset, used to bias domain values
// to minimize loss of precision when converted to 32-bit
// floating point values for display.
this.subplot.setDomainOffset(updater.getDomainOffset());
// Draw the buffers. Select color by index.
this.subplot.getDrawingObject().lines =
updater.getLineBuffers().map(function (buf, i) {
return {
buffer: buf.getBuffer(),
color: PlotPalette.getFloatColor(i),
points: buf.getLength()
};
});
};
PlotOverlayMode.prototype.getSubPlots = function () {
return this.subplots;
};
PlotOverlayMode.prototype.isZoomed = function () {
return this.panZoomStack.getDepth() > 1;
};
PlotOverlayMode.prototype.stepBackPanZoom = function () {
this.panZoomStack.popPanZoom();
this.subplot.update();
};
PlotOverlayMode.prototype.unzoom = function () {
this.panZoomStack.clearPanZoom();
this.subplot.update();
};
return PlotOverlayMode;
}
);

View File

@ -1,104 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../SubPlot", "../elements/PlotPalette", "../elements/PlotPanZoomStackGroup"],
function (SubPlot, PlotPalette, PlotPanZoomStackGroup) {
/**
* Handles plotting in Stacked mode. In stacked mode, there
* is one sub-plot for each plotted object.
* @memberof platform/features/plot
* @constructor
* @implements {platform/features/plot.PlotModeHandler}
* @param {DomainObject[]} the domain objects to be plotted
*/
function PlotStackMode(telemetryObjects, subPlotFactory) {
var self = this;
this.panZoomStackGroup =
new PlotPanZoomStackGroup(telemetryObjects.length);
this.subplots = telemetryObjects.map(function (telemetryObject, i) {
return subPlotFactory.createSubPlot(
[telemetryObject],
self.panZoomStackGroup.getPanZoomStack(i)
);
});
}
PlotStackMode.prototype.plotTelemetryTo = function (subplot, prepared, index) {
var buffer = prepared.getLineBuffers()[index];
// Track the domain offset, used to bias domain values
// to minimize loss of precision when converted to 32-bit
// floating point values for display.
subplot.setDomainOffset(prepared.getDomainOffset());
// Draw the buffers. Always use the 0th color, because there
// is one line per plot.
subplot.getDrawingObject().lines = [{
buffer: buffer.getBuffer(),
color: PlotPalette.getFloatColor(0),
points: buffer.getLength()
}];
};
PlotStackMode.prototype.plotTelemetry = function (prepared) {
var self = this;
// Fit to the boundaries of the data, but don't
// override any user-initiated pan-zoom changes.
this.panZoomStackGroup.setBasePanZoom(
prepared.getOrigin(),
prepared.getDimensions()
);
this.subplots.forEach(function (subplot, index) {
self.plotTelemetryTo(subplot, prepared, index);
});
};
PlotStackMode.prototype.getSubPlots = function () {
return this.subplots;
};
PlotStackMode.prototype.isZoomed = function () {
return this.panZoomStackGroup.getDepth() > 1;
};
PlotStackMode.prototype.stepBackPanZoom = function () {
this.panZoomStackGroup.popPanZoom();
this.subplots.forEach(function (subplot) {
subplot.update();
});
};
PlotStackMode.prototype.unzoom = function () {
this.panZoomStackGroup.clearPanZoom();
this.subplots.forEach(function (subplot) {
subplot.update();
});
};
return PlotStackMode;
}
);

View File

@ -1,95 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/Canvas2DChart"],
function (Canvas2DChart) {
describe("A canvas 2d chart", function () {
var mockCanvas,
mock2d,
chart;
beforeEach(function () {
mockCanvas = jasmine.createSpyObj("canvas", ["getContext"]);
mock2d = jasmine.createSpyObj(
"2d",
[
"clearRect",
"beginPath",
"moveTo",
"lineTo",
"stroke",
"fillRect"
]
);
mockCanvas.getContext.andReturn(mock2d);
chart = new Canvas2DChart(mockCanvas);
});
// Note that tests below are less specific than they
// could be, esp. w.r.t. arguments to drawing calls;
// this is a fallback option so is a lower test priority.
it("allows the canvas to be cleared", function () {
chart.clear();
expect(mock2d.clearRect).toHaveBeenCalled();
});
it("does not construct if 2D is unavailable", function () {
mockCanvas.getContext.andReturn(undefined);
expect(function () {
return new Canvas2DChart(mockCanvas);
}).toThrow();
});
it("allows dimensions to be set", function () {
// No return value, just verify API is present
chart.setDimensions([120, 120], [0, 10]);
});
it("allows lines to be drawn", function () {
var testBuffer = [0, 1, 3, 8],
testColor = [0.25, 0.33, 0.66, 1.0],
testPoints = 2;
chart.drawLine(testBuffer, testColor, testPoints);
expect(mock2d.beginPath).toHaveBeenCalled();
expect(mock2d.lineTo.calls.length).toEqual(1);
expect(mock2d.stroke).toHaveBeenCalled();
});
it("allows squares to be drawn", function () {
var testMin = [0, 1],
testMax = [10, 10],
testColor = [0.25, 0.33, 0.66, 1.0];
chart.drawSquare(testMin, testMax, testColor);
expect(mock2d.fillRect).toHaveBeenCalled();
});
});
}
);

View File

@ -1,143 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/GLChart"],
function (GLChart) {
describe("A WebGL chart", function () {
var mockCanvas,
mockGL,
glChart;
beforeEach(function () {
mockCanvas = jasmine.createSpyObj("canvas", ["getContext"]);
mockGL = jasmine.createSpyObj(
"gl",
[
"createShader",
"compileShader",
"shaderSource",
"attachShader",
"createProgram",
"linkProgram",
"useProgram",
"enableVertexAttribArray",
"getAttribLocation",
"getUniformLocation",
"createBuffer",
"lineWidth",
"enable",
"blendFunc",
"viewport",
"clear",
"uniform2fv",
"uniform4fv",
"bufferData",
"bindBuffer",
"vertexAttribPointer",
"drawArrays"
]
);
mockGL.ARRAY_BUFFER = "ARRAY_BUFFER";
mockGL.DYNAMIC_DRAW = "DYNAMIC_DRAW";
mockGL.TRIANGLE_FAN = "TRIANGLE_FAN";
mockGL.LINE_STRIP = "LINE_STRIP";
// Echo back names for uniform locations, so we can
// test which of these are set for certain operations.
mockGL.getUniformLocation.andCallFake(function (a, name) {
return name;
});
mockCanvas.getContext.andReturn(mockGL);
glChart = new GLChart(mockCanvas);
});
it("allows the canvas to be cleared", function () {
glChart.clear();
expect(mockGL.clear).toHaveBeenCalled();
});
it("does not construct if WebGL is unavailable", function () {
mockCanvas.getContext.andReturn(undefined);
expect(function () {
return new GLChart(mockCanvas);
}).toThrow();
});
it("allows dimensions to be set", function () {
glChart.setDimensions([120, 120], [0, 10]);
expect(mockGL.uniform2fv)
.toHaveBeenCalledWith("uDimensions", [120, 120]);
expect(mockGL.uniform2fv)
.toHaveBeenCalledWith("uOrigin", [0, 10]);
});
it("allows lines to be drawn", function () {
var testBuffer = [0, 1, 3, 8],
testColor = [0.25, 0.33, 0.66, 1.0],
testPoints = 2;
glChart.drawLine(testBuffer, testColor, testPoints);
expect(mockGL.bufferData).toHaveBeenCalledWith(
mockGL.ARRAY_BUFFER,
testBuffer,
mockGL.DYNAMIC_DRAW
);
expect(mockGL.uniform4fv)
.toHaveBeenCalledWith("uColor", testColor);
expect(mockGL.drawArrays)
.toHaveBeenCalledWith("LINE_STRIP", 0, testPoints);
});
it("allows squares to be drawn", function () {
var testMin = [0, 1],
testMax = [10, 10],
testColor = [0.25, 0.33, 0.66, 1.0];
glChart.drawSquare(testMin, testMax, testColor);
expect(mockGL.uniform4fv)
.toHaveBeenCalledWith("uColor", testColor);
expect(mockGL.drawArrays)
.toHaveBeenCalledWith("TRIANGLE_FAN", 0, 4);
});
it("uses buffer sizes reported by WebGL", function () {
// Make sure that GLChart uses the GL buffer size, which may
// differ from what canvas requested. WTD-852
mockCanvas.width = 300;
mockCanvas.height = 150;
mockGL.drawingBufferWidth = 200;
mockGL.drawingBufferHeight = 175;
glChart.clear();
expect(mockGL.viewport).toHaveBeenCalledWith(0, 0, 200, 175);
});
});
}
);

View File

@ -1,216 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/MCTChart"],
function (MCTChart) {
describe("The mct-chart directive", function () {
var mockInterval,
mockLog,
mockScope,
mockElement,
mockCanvas,
mockGL,
mockC2d,
mockPromise,
mctChart;
beforeEach(function () {
mockInterval =
jasmine.createSpy("$interval");
mockLog =
jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
mockScope = jasmine.createSpyObj(
"$scope",
["$watchCollection", "$on", "$apply"]
);
mockElement =
jasmine.createSpyObj("element", ["find", "html"]);
mockInterval.cancel = jasmine.createSpy("cancelInterval");
mockPromise = jasmine.createSpyObj("promise", ["then"]);
// mct-chart uses GLChart, so it needs WebGL API
mockCanvas =
jasmine.createSpyObj("canvas", ["getContext", "addEventListener"]);
mockGL = jasmine.createSpyObj(
"gl",
[
"createShader",
"compileShader",
"shaderSource",
"attachShader",
"createProgram",
"linkProgram",
"useProgram",
"enableVertexAttribArray",
"getAttribLocation",
"getUniformLocation",
"createBuffer",
"lineWidth",
"enable",
"blendFunc",
"viewport",
"clear",
"uniform2fv",
"uniform4fv",
"bufferData",
"bindBuffer",
"vertexAttribPointer",
"drawArrays"
]
);
mockC2d = jasmine.createSpyObj('c2d', ['clearRect']);
mockGL.ARRAY_BUFFER = "ARRAY_BUFFER";
mockGL.DYNAMIC_DRAW = "DYNAMIC_DRAW";
mockGL.TRIANGLE_FAN = "TRIANGLE_FAN";
mockGL.LINE_STRIP = "LINE_STRIP";
// Echo back names for uniform locations, so we can
// test which of these are set for certain operations.
mockGL.getUniformLocation.andCallFake(function (a, name) {
return name;
});
mockElement.find.andReturn([mockCanvas]);
mockCanvas.getContext.andCallFake(function (type) {
return { webgl: mockGL, '2d': mockC2d }[type];
});
mockInterval.andReturn(mockPromise);
mctChart = new MCTChart(mockInterval, mockLog);
});
it("is applicable at the element level", function () {
expect(mctChart.restrict).toEqual("E");
});
it("places a 'draw' attribute in-scope", function () {
// Should ask Angular for the draw attribute
expect(mctChart.scope.draw).toEqual("=");
});
it("watches for changes in the drawn object", function () {
mctChart.link(mockScope, mockElement);
expect(mockScope.$watchCollection)
.toHaveBeenCalledWith("draw", jasmine.any(Function));
});
it("issues one draw call per line", function () {
mctChart.link(mockScope, mockElement);
mockScope.$watchCollection.mostRecentCall.args[1]({
lines: [{}, {}, {}]
});
expect(mockGL.drawArrays.calls.length).toEqual(3);
});
it("issues one draw call per box", function () {
mctChart.link(mockScope, mockElement);
mockScope.$watchCollection.mostRecentCall.args[1]({
boxes: [
{ start: [0, 0], end: [1, 1] },
{ start: [0, 0], end: [1, 1] },
{ start: [0, 0], end: [1, 1] },
{ start: [0, 0], end: [1, 1] }
]
});
expect(mockGL.drawArrays.calls.length).toEqual(4);
});
it("does not fail if no draw object is in scope", function () {
mctChart.link(mockScope, mockElement);
expect(mockScope.$watchCollection.mostRecentCall.args[1])
.not.toThrow();
});
it("draws on canvas resize", function () {
mctChart.link(mockScope, mockElement);
// Should track canvas size in an interval
expect(mockInterval).toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Number),
0,
false
);
// Verify pre-condition
expect(mockGL.clear).not.toHaveBeenCalled();
mockCanvas.width = 100;
mockCanvas.offsetWidth = 150;
mockCanvas.height = 200;
mockCanvas.offsetHeight = 200;
mockInterval.mostRecentCall.args[0]();
// Use clear as an indication that drawing has occurred
expect(mockGL.clear).toHaveBeenCalled();
});
it("warns if no WebGL context is available", function () {
mockCanvas.getContext.andReturn(undefined);
mctChart.link(mockScope, mockElement);
expect(mockLog.warn).toHaveBeenCalled();
});
it("falls back to Canvas 2d API if WebGL context is lost", function () {
mctChart.link(mockScope, mockElement);
expect(mockCanvas.addEventListener)
.toHaveBeenCalledWith("webglcontextlost", jasmine.any(Function));
expect(mockCanvas.getContext).not.toHaveBeenCalledWith('2d');
mockCanvas.addEventListener.mostRecentCall.args[1]();
expect(mockCanvas.getContext).toHaveBeenCalledWith('2d');
});
it("logs nothing in nominal situations (WebGL available)", function () {
// Complement the previous test
mctChart.link(mockScope, mockElement);
expect(mockLog.warn).not.toHaveBeenCalled();
});
// Avoid resource leaks
it("stops polling for size changes on destroy", function () {
mctChart.link(mockScope, mockElement);
// Should be listening for a destroy event
expect(mockScope.$on).toHaveBeenCalledWith(
"$destroy",
jasmine.any(Function)
);
// Precondition - interval still active
expect(mockInterval.cancel).not.toHaveBeenCalled();
// Broadcast a $destroy
mockScope.$on.mostRecentCall.args[1]();
// Should have stopped the interval
expect(mockInterval.cancel).toHaveBeenCalledWith(mockPromise);
});
});
}
);

View File

@ -1,403 +0,0 @@
/*global angular*/
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/PlotController"],
function (PlotController) {
describe("The plot controller", function () {
var mockScope,
mockElement,
mockExportImageService,
mockFormatter,
mockHandler,
mockThrottle,
mockHandle,
mockDomainObject,
mockSeries,
mockStatusCapability,
controller,
mockConductor;
function bind(method, thisObj) {
return function () {
return method.apply(thisObj, arguments);
};
}
function fireEvent(name, args) {
mockScope.$on.calls.forEach(function (call) {
if (call.args[0] === name) {
call.args[1].apply(null, args || []);
}
});
}
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1].apply(null, [value]);
}
});
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$watch", "$on", "$emit"]
);
mockElement = angular.element('<div />');
mockExportImageService = jasmine.createSpyObj(
"ExportImageService",
["exportJPG", "exportPNG"]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability", "hasCapability"]
);
mockHandler = jasmine.createSpyObj(
"telemetrySubscriber",
["handle"]
);
mockThrottle = jasmine.createSpy("throttle");
mockHandle = jasmine.createSpyObj(
"subscription",
[
"unsubscribe",
"getTelemetryObjects",
"getMetadata",
"getDomainValue",
"getRangeValue",
"getDatum",
"request"
]
);
mockSeries = jasmine.createSpyObj(
'series',
['getPointCount', 'getDomainValue', 'getRangeValue']
);
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
["set"]
);
mockHandler.handle.andReturn(mockHandle);
mockThrottle.andCallFake(function (fn) {
return fn;
});
mockHandle.getTelemetryObjects.andReturn([mockDomainObject]);
mockHandle.getMetadata.andReturn([{}]);
mockHandle.getDomainValue.andReturn(123);
mockHandle.getRangeValue.andReturn(42);
mockScope.domainObject = mockDomainObject;
mockConductor = jasmine.createSpyObj('conductor', [
'on',
'off',
'bounds',
'timeSystem',
'timeOfInterest',
'follow'
]);
mockConductor.bounds.andReturn({});
controller = new PlotController(
mockScope,
mockElement,
mockExportImageService,
mockFormatter,
mockHandler,
mockThrottle,
undefined,
{time: mockConductor}
);
});
it("provides plot colors", function () {
// PlotPalette will have its own tests
expect(controller.getColor(0))
.toEqual(jasmine.any(String));
// Colors should be unique
expect(controller.getColor(0))
.not.toEqual(controller.getColor(1));
});
it("subscribes to telemetry when a domain object appears in scope", function () {
// Make sure we're using the right watch here
expect(mockScope.$watch.mostRecentCall.args[0])
.toEqual("domainObject");
// Make an object available
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Should have subscribed
expect(mockHandler.handle).toHaveBeenCalledWith(
mockDomainObject,
jasmine.any(Function),
true // Lossless
);
});
it("draws lines when data becomes available", function () {
// Make an object available
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Verify precondition
controller.getSubPlots().forEach(function (subplot) {
expect(subplot.getDrawingObject().lines)
.not.toBeDefined();
});
// Make sure there actually are subplots being verified
expect(controller.getSubPlots().length > 0).toBeTruthy();
// Broadcast data
mockHandler.handle.mostRecentCall.args[1]();
controller.getSubPlots().forEach(function (subplot) {
expect(subplot.getDrawingObject().lines)
.toBeDefined();
});
});
it("unsubscribes when domain object changes", function () {
// Make an object available
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Verify precondition - shouldn't unsubscribe yet
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
// Remove the domain object
mockScope.$watch.mostRecentCall.args[1](undefined);
// Should have unsubscribed
expect(mockHandle.unsubscribe).toHaveBeenCalled();
});
it("changes modes depending on number of objects", function () {
// Act like one object is available
mockHandle.getTelemetryObjects.andReturn([
mockDomainObject
]);
// Make an object available; invoke handler's callback
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
mockHandler.handle.mostRecentCall.args[1]();
expect(controller.getModeOptions().length).toEqual(1);
// Act like one object is available
mockHandle.getTelemetryObjects.andReturn([
mockDomainObject,
mockDomainObject,
mockDomainObject
]);
// Make an object available; invoke handler's callback
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
mockHandler.handle.mostRecentCall.args[1]();
expect(controller.getModeOptions().length).toEqual(2);
});
// Interface tests follow; these will be delegated (mostly
// to PlotModeOptions, which is tested separately).
it("provides access to available plot mode options", function () {
expect(Array.isArray(controller.getModeOptions()))
.toBeTruthy();
});
it("provides a current plot mode", function () {
expect(controller.getMode().name)
.toEqual(jasmine.any(String));
});
it("allows plot mode to be changed", function () {
expect(function () {
controller.setMode(controller.getMode());
}).not.toThrow();
});
it("provides an array of sub-plots", function () {
expect(Array.isArray(controller.getSubPlots()))
.toBeTruthy();
});
it("allows plots to be updated", function () {
expect(bind(controller.update, controller)).not.toThrow();
});
it("allows changing pan-zoom state", function () {
expect(bind(controller.isZoomed, controller)).not.toThrow();
expect(bind(controller.stepBackPanZoom, controller)).not.toThrow();
expect(bind(controller.unzoom, controller)).not.toThrow();
});
it("sets status when plot becomes detached from time conductor", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
function boundsEvent() {
fireEvent("telemetry:display:bounds", [
{},
{ start: 10, end: 100 },
true
]);
}
mockDomainObject.hasCapability.andCallFake(function (name) {
return name === "status";
});
mockDomainObject.getCapability.andReturn(mockStatusCapability);
spyOn(controller, "isZoomed");
//Mock zoomed in state
controller.isZoomed.andReturn(true);
boundsEvent();
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", true);
//"Reset" zoom
controller.isZoomed.andReturn(false);
boundsEvent();
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", false);
});
it("indicates if a request is pending", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(controller.isRequestPending()).toBeTruthy();
mockHandle.request.mostRecentCall.args[1](
mockDomainObject,
mockSeries
);
expect(controller.isRequestPending()).toBeFalsy();
});
it("requests historical telemetry", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockHandle.request).toHaveBeenCalled();
mockHandle.request.mostRecentCall.args[1](
mockDomainObject,
mockSeries
);
});
it("unsubscribes when destroyed", function () {
// Make an object available
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Make sure $destroy is what's listened for
expect(mockScope.$on.mostRecentCall.args[0]).toEqual('$destroy');
// Also verify precondition
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
// Destroy the scope
fireEvent("$destroy");
// Should have unsubscribed
expect(mockHandle.unsubscribe).toHaveBeenCalled();
});
it("requeries when displayable bounds change", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockHandle.request.calls.length).toEqual(1);
fireEvent("telemetry:display:bounds", [
{},
{ start: 10, end: 100 }
]);
expect(mockHandle.request.calls.length).toEqual(2);
});
it("requeries when user changes domain selection", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockHandle.request.calls.length).toEqual(1);
fireWatch("axes[0].active.key", 'someNewKey');
expect(mockHandle.request.calls.length).toEqual(2);
});
it("requeries when user changes range selection", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockHandle.request.calls.length).toEqual(1);
fireWatch("axes[1].active.key", 'someNewKey');
expect(mockHandle.request.calls.length).toEqual(2);
});
it("maintains externally-provided domain axis bounds after data is received", function () {
mockSeries.getPointCount.andReturn(3);
mockSeries.getRangeValue.andReturn(42);
mockSeries.getDomainValue.andCallFake(function (i) {
return 2500 + i * 2500;
});
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
fireEvent("telemetry:display:bounds", [
{},
{start: 0, end: 10000}
]);
mockHandle.request.mostRecentCall.args[1](
mockDomainObject,
mockSeries
);
// Pan-zoom state should reflect bounds set externally;
// domain axis should not have shrunk to fit data.
expect(
controller.getSubPlots()[0].panZoomStack.getOrigin()[0]
).toEqual(0);
expect(
controller.getSubPlots()[0].panZoomStack.getDimensions()[0]
).toEqual(10000);
});
it("provides classes for legends based on limit state", function () {
var mockTelemetryObjects = mockHandle.getTelemetryObjects();
mockHandle.getDatum.andReturn({});
mockTelemetryObjects.forEach(function (mockObject, i) {
var id = 'object-' + i,
mockLimitCapability =
jasmine.createSpyObj('limit-' + id, ['evaluate']);
mockObject.getId.andReturn(id);
mockObject.getCapability.andCallFake(function (key) {
return (key === 'limit') && mockLimitCapability;
});
mockLimitCapability.evaluate
.andReturn({ cssClass: 'alarm-' + id });
});
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
mockHandler.handle.mostRecentCall.args[1]();
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
expect(controller.getLegendClass(mockTelemetryObject))
.toEqual('alarm-' + mockTelemetryObject.getId());
});
});
});
}
);

View File

@ -1,147 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
['../src/PlotOptionsController'],
function (PlotOptionsController) {
describe("The Plot Options controller", function () {
var plotOptionsController,
mockDomainObject,
mockMutationCapability,
mockUseCapabilities,
mockCompositionCapability,
mockComposition,
mockUnlisten,
mockChildOne,
mockChildTwo,
model,
mockScope;
beforeEach(function () {
model = {
composition: ['childOne']
};
mockChildOne = jasmine.createSpyObj('domainObject', [
'getId'
]);
mockChildOne.getId.andReturn('childOne');
mockChildTwo = jasmine.createSpyObj('childTwo', [
'getId'
]);
mockChildOne.getId.andReturn('childTwo');
mockCompositionCapability = jasmine.createSpyObj('compositionCapability', [
'then'
]);
mockComposition = [
mockChildOne
];
mockCompositionCapability.then.andCallFake(function (callback) {
callback(mockComposition);
});
mockUseCapabilities = jasmine.createSpyObj('useCapabilities', [
'composition',
'mutation'
]);
mockUseCapabilities.composition.andReturn(mockCompositionCapability);
mockMutationCapability = jasmine.createSpyObj('mutationCapability', [
'listen'
]);
mockUnlisten = jasmine.createSpy('unlisten');
mockMutationCapability.listen.andReturn(mockUnlisten);
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getModel',
'useCapability',
'getCapability'
]);
mockDomainObject.useCapability.andCallFake(function (capability) {
return mockUseCapabilities[capability]();
});
mockDomainObject.getCapability.andReturn(mockMutationCapability);
mockDomainObject.getModel.andReturn(model);
mockScope = jasmine.createSpyObj('scope', [
'$on',
'$watchCollection'
]);
mockScope.domainObject = mockDomainObject;
function noop() {}
mockScope.$watchCollection.andReturn(noop);
plotOptionsController = new PlotOptionsController(mockScope);
});
it("sets form definitions on scope", function () {
expect(mockScope.xAxisForm).toBeDefined();
expect(mockScope.yAxisForm).toBeDefined();
expect(mockScope.plotSeriesForm).toBeDefined();
});
it("sets object children on scope", function () {
expect(mockScope.children).toBe(mockComposition);
});
it("on changes in object composition, updates the form", function () {
expect(mockMutationCapability.listen).toHaveBeenCalled();
expect(mockScope.children).toBe(mockComposition);
expect(mockScope.children.length).toBe(1);
mockComposition.push(mockChildTwo);
model.composition.push('childTwo');
mockMutationCapability.listen.mostRecentCall.args[0](model);
expect(mockScope.children).toBe(mockComposition);
expect(mockScope.children.length).toBe(2);
});
it("on changes in form values, updates the object model", function () {
var scopeConfiguration = mockScope.configuration,
objModel = mockDomainObject.getModel();
scopeConfiguration.plot.yAxis.autoScale = true;
scopeConfiguration.plot.yAxis.key = 'eu';
scopeConfiguration.plot.xAxis.key = 'lst';
expect(mockScope.$watchCollection).toHaveBeenCalled();
mockScope.$watchCollection.calls[0].args[1]();
expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function));
mockDomainObject.useCapability.mostRecentCall.args[1](objModel);
expect(objModel.configuration.plot.yAxis.autoScale).toBe(true);
expect(objModel.configuration.plot.yAxis.key).toBe('eu');
expect(objModel.configuration.plot.xAxis.key).toBe('lst');
});
it("cleans up listeners on destruction of the controller", function () {
mockScope.$on.mostRecentCall.args[1]();
expect(mockUnlisten).toHaveBeenCalled();
});
});
}
);

View File

@ -1,66 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/SubPlotFactory"],
function (SubPlotFactory) {
describe("The sub-plot factory", function () {
var mockDomainObject,
mockPanZoomStack,
mockFormatter,
factory;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockPanZoomStack = jasmine.createSpyObj(
"panZoomStack",
["getPanZoom"]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
mockPanZoomStack.getPanZoom.andReturn({
origin: [0, 0],
dimensions: [100, 100]
});
factory = new SubPlotFactory(mockFormatter);
});
it("creates sub-plots", function () {
expect(factory.createSubPlot(
[mockDomainObject],
mockPanZoomStack
).getTelemetryObjects()).toEqual([mockDomainObject]);
});
});
}
);

View File

@ -1,208 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/SubPlot"],
function (SubPlot) {
describe("A sub-plot", function () {
var mockDomainObject,
mockPanZoomStack,
mockFormatter,
mockElement,
testDomainObjects,
testOrigin,
testDimensions,
subplot;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockPanZoomStack = jasmine.createSpyObj(
"panZoomStack",
[
"getDepth",
"pushPanZoom",
"popPanZoom",
"setBasePanZoom",
"clearPanZoom",
"getPanZoom",
"getOrigin",
"getDimensions"
]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
mockElement = jasmine.createSpyObj(
"element",
["getBoundingClientRect"]
);
testOrigin = [5, 10];
testDimensions = [3000, 1000];
testDomainObjects = [mockDomainObject, mockDomainObject];
mockPanZoomStack.getOrigin.andReturn(testOrigin);
mockPanZoomStack.getDimensions.andReturn(testDimensions);
mockPanZoomStack.getPanZoom.andReturn(
{ origin: testOrigin, dimensions: testDimensions }
);
mockElement.getBoundingClientRect.andReturn(
{ left: 10, top: 20, width: 100, height: 100 }
);
subplot = new SubPlot(
testDomainObjects,
mockPanZoomStack,
mockFormatter
);
});
it("provides a getter for its plotted objects", function () {
expect(subplot.getTelemetryObjects())
.toEqual(testDomainObjects);
});
it("exposes tick marks", function () {
// Just test availability; details are tested
// in PlotTickFormatter
expect(Array.isArray(subplot.getDomainTicks()))
.toBeTruthy();
expect(Array.isArray(subplot.getRangeTicks()))
.toBeTruthy();
});
it("allows hovering state to be tracked", function () {
expect(subplot.isHovering()).toBeFalsy();
expect(subplot.isHovering(true)).toBeTruthy();
expect(subplot.isHovering()).toBeTruthy();
expect(subplot.isHovering(false)).toBeFalsy();
expect(subplot.isHovering()).toBeFalsy();
});
it("provides hovering coordinates", function () {
// Should be empty when not hovering
expect(subplot.getHoverCoordinates())
.toBeUndefined();
// Start hovering
subplot.hover({ target: mockElement });
// Should now have coordinates to display
expect(subplot.getHoverCoordinates())
.toEqual(jasmine.any(String));
});
it("supports marquee zoom", function () {
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
// Simulate a marquee zoom. Note that the mockElement
// is 100 by 100 and starts at 10,20
subplot.startDrag({
target: mockElement,
clientX: 60,
clientY: 45
});
subplot.hover({
target: mockElement,
clientX: 75,
clientY: 85
});
subplot.endDrag({
target: mockElement,
clientX: 80,
clientY: 95
});
// ... so the origin should be 50%,25% into current dimensions,
// and new dimensions should be 20%,50% thereof
expect(mockPanZoomStack.pushPanZoom).toHaveBeenCalledWith(
[
testOrigin[0] + testDimensions[0] * 0.50,
testOrigin[1] + testDimensions[1] * 0.25
],
[
testDimensions[0] * 0.20,
testDimensions[1] * 0.50
]
);
});
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 () {
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
// Simulate a marquee zoom. Note that the mockElement
// is 100 by 100 and starts at 10,20
subplot.startDrag({
target: mockElement,
clientX: 60,
clientY: 45
});
subplot.hover({
target: mockElement,
clientX: 75,
clientY: 85
});
subplot.endDrag({
target: mockElement,
clientX: 60,
clientY: 45
});
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
});
it("provides access to a drawable object", function () {
expect(typeof subplot.getDrawingObject()).toEqual('object');
});
it("allows a domain offset to be provided", function () {
// Domain object is needed to adjust canvas coordinates
// to avoid loss-of-precision associated with converting
// to 32 bit floats.
subplot.setDomainOffset(3);
subplot.update();
// Should have adjusted the origin accordingly
expect(subplot.getDrawingObject().origin[0])
.toEqual(2);
});
});
}
);

View File

@ -1,107 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotAxis"],
function (PlotAxis) {
describe("A plot axis", function () {
var testMetadatas,
testDefault,
axis;
beforeEach(function () {
testMetadatas = [
{
tests: [
{ key: "t0", name: "T0" },
{ key: "t1", name: "T1" }
],
someKey: "some value"
},
{
tests: [
{ key: "t0", name: "T0" },
{ key: "t2", name: "T2" }
]
},
{
tests: [
{ key: "t3", name: "T3" },
{ key: "t4", name: "T4" },
{ key: "t5", name: "T5" },
{ key: "t6", name: "T6" }
]
}
];
testDefault = { key: "test", name: "Test" };
axis = new PlotAxis("tests", testMetadatas, testDefault);
});
it("pulls out a list of domain or range options", function () {
// Should have filtered out duplicates, etc
expect(axis.options).toEqual([
{ key: "t0", name: "T0" },
{ key: "t1", name: "T1" },
{ key: "t2", name: "T2" },
{ key: "t3", name: "T3" },
{ key: "t4", name: "T4" },
{ key: "t5", name: "T5" },
{ key: "t6", name: "T6" }
]);
});
it("chooses the first option as a default", function () {
expect(axis.active).toEqual({ key: "t0", name: "T0" });
});
it("falls back to a provided default if no options are present", function () {
expect(new PlotAxis("tests", [{}], testDefault).active)
.toEqual(testDefault);
});
it("allows options to be chosen by key", function () {
axis.chooseOption("t3");
expect(axis.active).toEqual({ key: "t3", name: "T3" });
});
it("reflects changes to applicable metadata", function () {
axis.updateMetadata([testMetadatas[1]]);
expect(axis.options).toEqual([
{ key: "t0", name: "T0" },
{ key: "t2", name: "T2" }
]);
});
it("returns the same array instance for unchanged metadata", function () {
// ...to avoid triggering extra digest cycles.
var oldInstance = axis.options;
axis.updateMetadata(testMetadatas);
expect(axis.options).toBe(oldInstance);
});
});
}
);

View File

@ -1,100 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../../src/elements/PlotLimitTracker"],
function (PlotLimitTracker) {
describe("A plot's limit tracker", function () {
var mockHandle,
testRange,
mockTelemetryObjects,
testData,
tracker;
beforeEach(function () {
testRange = "some-range";
testData = {};
mockHandle = jasmine.createSpyObj(
'handle',
['getTelemetryObjects', 'getDatum']
);
mockTelemetryObjects = ['a', 'b', 'c'].map(function (id, i) {
var mockTelemetryObject = jasmine.createSpyObj(
'object-' + id,
['getId', 'getCapability', 'getModel']
),
mockLimitCapability = jasmine.createSpyObj(
'limit-' + id,
['evaluate']
);
testData[id] = { id: id, value: i };
mockTelemetryObject.getId.andReturn(id);
mockTelemetryObject.getCapability.andCallFake(function (key) {
return key === 'limit' && mockLimitCapability;
});
mockLimitCapability.evaluate
.andReturn({ cssClass: 'alarm-' + id});
return mockTelemetryObject;
});
mockHandle.getTelemetryObjects.andReturn(mockTelemetryObjects);
mockHandle.getDatum.andCallFake(function (telemetryObject) {
return testData[telemetryObject.getId()];
});
tracker = new PlotLimitTracker(mockHandle, testRange);
});
it("initially provides no limit state", function () {
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
expect(tracker.getLegendClass(mockTelemetryObject))
.toBeUndefined();
});
});
describe("when asked to update", function () {
beforeEach(function () {
tracker.update();
});
it("evaluates limits using the limit capability", function () {
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
var id = mockTelemetryObject.getId(),
mockLimit =
mockTelemetryObject.getCapability('limit');
expect(mockLimit.evaluate)
.toHaveBeenCalledWith(testData[id], testRange);
});
});
it("exposes legend classes returned by the limit capability", function () {
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
var id = mockTelemetryObject.getId();
expect(tracker.getLegendClass(mockTelemetryObject))
.toEqual('alarm-' + id);
});
});
});
});
}
);

View File

@ -1,167 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotLineBuffer"],
function (PlotLineBuffer) {
var TEST_INITIAL_SIZE = 10,
TEST_MAX_SIZE = 40,
TEST_DOMAIN_OFFSET = 42;
describe("A plot line buffer", function () {
var mockSeries,
testDomainValues,
testRangeValues,
buffer;
beforeEach(function () {
testDomainValues = [1, 3, 7, 9, 14, 15];
testRangeValues = [8, 0, 3, 9, 8, 11];
mockSeries = jasmine.createSpyObj(
"series",
['getPointCount', 'getDomainValue', 'getRangeValue']
);
mockSeries.getPointCount.andCallFake(function () {
return testDomainValues.length;
});
mockSeries.getDomainValue.andCallFake(function (i) {
return testDomainValues[i];
});
mockSeries.getRangeValue.andCallFake(function (i) {
return testRangeValues[i];
});
buffer = new PlotLineBuffer(
TEST_DOMAIN_OFFSET,
TEST_INITIAL_SIZE,
TEST_MAX_SIZE
);
// Start with some data in there
buffer.insert(mockSeries, 0);
});
it("allows insertion of series data", function () {
// Convert to a regular array for checking.
// Verify that domain/ranges were interleaved and
// that domain offset was adjusted for.
expect(
Array.prototype.slice.call(buffer.getBuffer()).slice(0, 12)
).toEqual([-41, 8, -39, 0, -35, 3, -33, 9, -28, 8, -27, 11]);
expect(buffer.getLength()).toEqual(6);
});
it("finds insertion indexes", function () {
expect(buffer.findInsertionIndex(0)).toEqual(0);
expect(buffer.findInsertionIndex(2)).toEqual(1);
expect(buffer.findInsertionIndex(5)).toEqual(2);
expect(buffer.findInsertionIndex(10)).toEqual(4);
expect(buffer.findInsertionIndex(14.5)).toEqual(5);
expect(buffer.findInsertionIndex(20)).toEqual(6);
});
it("allows insertion in the middle", function () {
var head = [-41, 8, -39, 0, -35, 3],
tail = [-33, 9, -28, 8, -27, 11];
buffer.insert(mockSeries, 3);
expect(
Array.prototype.slice.call(buffer.getBuffer()).slice(0, 24)
).toEqual(head.concat(head).concat(tail).concat(tail));
expect(buffer.getLength()).toEqual(12);
});
it("allows values to be trimmed from the start", function () {
buffer.trim(2);
expect(buffer.getLength()).toEqual(4);
expect(
Array.prototype.slice.call(buffer.getBuffer()).slice(0, 8)
).toEqual([-35, 3, -33, 9, -28, 8, -27, 11]);
});
it("expands buffer when needed to accommodate more data", function () {
var i;
// Initial underlying buffer should be twice initial size...
// (Since each pair will take up two elements)
expect(buffer.getBuffer().length).toEqual(20);
// Should be able to insert 6 series of 6 points each
// (After that, we'll hit the test max of 40)
for (i = 1; i < 15; i += 1) {
expect(buffer.insertPoint(i * 10, Math.sin(i), i))
.toBeTruthy();
}
// Buffer should have expanded in the process
expect(buffer.getBuffer().length).toEqual(40);
// Push to maximum size just to make sure...
for (i = 1; i < 150; i += 1) {
buffer.insertPoint(i * 10, Math.sin(i), i);
}
expect(buffer.getBuffer().length).toEqual(80);
});
it("ensures a maximum size", function () {
var i;
// Should be able to insert 6 series of 6 points each
// (After that, we'll hit the test max of 40)
for (i = 1; i < 6; i += 1) {
expect(buffer.getLength()).toEqual(6 * i);
expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY))
.toBeTruthy();
}
// Should be maxed out now
expect(buffer.getLength()).toEqual(36);
expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY))
.toBeFalsy();
expect(buffer.getLength()).toEqual(36);
});
it("reduces buffer size when space is no longer needed", function () {
// Check that actual buffer is sized to the initial size
// (double TEST_INITIAL_SIZE, since two elements are needed per
// point; one for domain, one for range)
expect(buffer.getBuffer().length).toEqual(20);
// Should have 6 elements now... grow to 24
buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
// This should have doubled the actual buffer size
expect(buffer.getBuffer().length).toEqual(80);
// Remove some values
buffer.trim(20);
// Actual buffer size should have been reduced accordingly
expect(buffer.getBuffer().length).toBeLessThan(80);
});
});
}
);

View File

@ -1,133 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../../src/elements/PlotLine"],
function (PlotLine) {
describe("A plot line", function () {
var mockBuffer,
mockSeries,
testDomainBuffer,
testRangeBuffer,
testSeries,
line;
beforeEach(function () {
testDomainBuffer = [];
testRangeBuffer = [];
testSeries = [];
mockBuffer = jasmine.createSpyObj(
'buffer',
['findInsertionIndex', 'insert', 'insertPoint', 'trim']
);
mockSeries = jasmine.createSpyObj(
'series',
['getPointCount', 'getDomainValue', 'getRangeValue']
);
mockSeries.getPointCount.andCallFake(function () {
return testSeries.length;
});
mockSeries.getDomainValue.andCallFake(function (i) {
return (testSeries[i] || [])[0];
});
mockSeries.getRangeValue.andCallFake(function (i) {
return (testSeries[i] || [])[1];
});
// Function like PlotLineBuffer, to aid in testability
mockBuffer.findInsertionIndex.andCallFake(function (v) {
var index = 0;
if (testDomainBuffer.indexOf(v) !== -1) {
return -1;
}
while ((index < testDomainBuffer.length) &&
(testDomainBuffer[index] < v)) {
index += 1;
}
return index;
});
mockBuffer.insert.andCallFake(function (series, index) {
var domains = [], ranges = [], i;
for (i = 0; i < series.getPointCount(); i += 1) {
domains.push(series.getDomainValue(i));
ranges.push(series.getRangeValue(i));
}
testDomainBuffer = testDomainBuffer.slice(0, index)
.concat(domains)
.concat(testDomainBuffer.slice(index));
testRangeBuffer = testRangeBuffer.slice(0, index)
.concat(ranges)
.concat(testRangeBuffer.slice(index));
return true;
});
mockBuffer.insertPoint.andCallFake(function (dv, rv, index) {
testDomainBuffer.splice(index, 0, dv);
testRangeBuffer.splice(index, 0, rv);
return true;
});
line = new PlotLine(mockBuffer);
});
it("allows single point insertion", function () {
line.addPoint(100, 200);
line.addPoint(50, 42);
line.addPoint(150, 12321);
// Should have managed insertion index choices to get to...
expect(testDomainBuffer).toEqual([50, 100, 150]);
expect(testRangeBuffer).toEqual([42, 200, 12321]);
});
it("allows series insertion", function () {
testSeries = [[50, 42], [100, 200], [150, 12321]];
line.addSeries(mockSeries);
// Should have managed insertion index choices to get to...
expect(testDomainBuffer).toEqual([50, 100, 150]);
expect(testRangeBuffer).toEqual([42, 200, 12321]);
});
it("splits series insertion when necessary", function () {
testSeries = [[50, 42], [100, 200], [150, 12321]];
line.addPoint(75, 1);
line.addSeries(mockSeries);
// Should have managed insertion index choices to get to...
expect(testDomainBuffer).toEqual([50, 75, 100, 150]);
expect(testRangeBuffer).toEqual([42, 1, 200, 12321]);
});
it("attempts to remove points when insertion fails", function () {
// Verify precondition - normally doesn't try to trim
line.addPoint(1, 2);
expect(mockBuffer.trim).not.toHaveBeenCalled();
// But if insertPoint fails, it should trim
mockBuffer.insertPoint.andReturn(false);
line.addPoint(2, 3);
expect(mockBuffer.trim).toHaveBeenCalled();
});
});
}
);

View File

@ -1,123 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotPalette"],
function (PlotPalette) {
describe("The plot palette", function () {
it("can be used as a constructor", function () {
// PlotPalette has all static methods, so make
// sure it returns itself if used as a constructor.
expect(new PlotPalette()).toBe(PlotPalette);
});
it("has 30 unique colors in an integer format", function () {
// Integer format may be useful internal to the application.
// RGB 0-255
var i, j;
// Used to verify one of R, G, B in loop below
function verifyChannel(c) {
expect(typeof c).toEqual("number");
expect(c <= 255).toBeTruthy();
expect(c >= 0).toBeTruthy();
}
for (i = 0; i < 30; i += 1) {
// Verify that we got an array of numbers
expect(Array.isArray(PlotPalette.getIntegerColor(i)))
.toBeTruthy();
expect(PlotPalette.getIntegerColor(i).length).toEqual(3);
// Verify all three channels for type and range
PlotPalette.getIntegerColor(i).forEach(verifyChannel);
// Verify uniqueness
for (j = i + 1; j < 30; j += 1) {
expect(PlotPalette.getIntegerColor(i)).not.toEqual(
PlotPalette.getIntegerColor(j)
);
}
}
});
it("has 30 unique colors in a floating-point format", function () {
// Float format is useful to WebGL.
// RGB 0.0-1.1
var i, j;
// Used to verify one of R, G, B in loop below
function verifyChannel(c) {
expect(typeof c).toEqual("number");
expect(c <= 1.0).toBeTruthy();
expect(c >= 0.0).toBeTruthy();
}
for (i = 0; i < 30; i += 1) {
// Verify that we got an array of numbers
expect(Array.isArray(PlotPalette.getFloatColor(i)))
.toBeTruthy();
expect(PlotPalette.getFloatColor(i).length).toEqual(4);
// Verify all three channels for type and range
PlotPalette.getFloatColor(i).forEach(verifyChannel);
// Verify uniqueness
for (j = i + 1; j < 30; j += 1) {
expect(PlotPalette.getFloatColor(i)).not.toEqual(
PlotPalette.getFloatColor(j)
);
}
}
});
it("has 30 unique colors in a string format", function () {
// String format is useful in stylesheets
// #RRGGBB in hex
var i, j, c;
for (i = 0; i < 30; i += 1) {
c = PlotPalette.getStringColor(i);
// Verify that we #-style color strings
expect(typeof c).toEqual('string');
expect(c.length).toEqual(7);
expect(/^#[0-9a-fA-F]+$/.test(c)).toBeTruthy();
// Verify uniqueness
for (j = i + 1; j < 30; j += 1) {
expect(PlotPalette.getStringColor(i)).not.toEqual(
PlotPalette.getStringColor(j)
);
}
}
});
});
}
);

View File

@ -1,126 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotPanZoomStackGroup"],
function (PlotPanZoomStackGroup) {
var COUNT = 8;
describe("A plot pan-zoom stack group", function () {
var stacks,
group;
beforeEach(function () {
group = new PlotPanZoomStackGroup(COUNT);
stacks = [];
while (stacks.length < COUNT) {
stacks.push(group.getPanZoomStack(stacks.length));
}
});
it("creates a number of separate stacks", function () {
expect(group.getPanZoomStack(0)).toBeDefined();
expect(group.getPanZoomStack(COUNT - 1)).toBeDefined();
expect(group.getPanZoomStack(COUNT)).toBeUndefined();
});
it("synchronizes pan-zoom stack depth", function () {
expect(group.getDepth()).toEqual(1);
group.getPanZoomStack(1).pushPanZoom([10, 20], [30, 40]);
stacks.forEach(function (stack) {
expect(stack.getDepth()).toEqual(2);
});
});
it("synchronizes domain but not range", function () {
// Set up different initial states
stacks.forEach(function (stack, i) {
stack.pushPanZoom([i, i], [i, i]);
});
// Push a new pan-zoom state onto one of the stacks
group.getPanZoomStack(1).pushPanZoom([99, 99], [42, 42]);
// Should changed domain values for all stacks, but
// only changed range values for stack 1
stacks.forEach(function (stack, i) {
expect(stack.getOrigin())
.toEqual([99, i === 1 ? 99 : i]);
expect(stack.getDimensions())
.toEqual([42, i === 1 ? 42 : i]);
});
});
it("synchronizes base pan-zoom", function () {
group.setBasePanZoom([10, 9], [8, 7]);
stacks.forEach(function (stack) {
expect(stack.getOrigin()).toEqual([10, 9]);
expect(stack.getDimensions()).toEqual([8, 7]);
});
});
it("clears pan-zoom on request", function () {
// Set up different initial states
stacks.forEach(function (stack, i) {
stack.pushPanZoom([i, i], [i, i]);
});
// Verify that we have a greater depth
expect(group.getDepth() > 1).toBeTruthy();
// Clear the pan-zoom state
group.clearPanZoom();
// Should be back down to our initial state
expect(group.getDepth()).toEqual(1);
stacks.forEach(function (stack) {
expect(stack.getDepth()).toEqual(1);
});
});
it("pops pan-zoom on request", function () {
// Set up different initial states
stacks.forEach(function (stack, i) {
stack.pushPanZoom([i, i], [i, i]);
});
// Verify that we have a greater depth
expect(group.getDepth()).toEqual(COUNT + 1);
// Clear the pan-zoom state
group.popPanZoom();
// Should be back down to our initial state
expect(group.getDepth()).toEqual(COUNT);
stacks.forEach(function (stack) {
expect(stack.getDepth()).toEqual(COUNT);
});
});
});
}
);

View File

@ -1,99 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotPanZoomStack"],
function (PlotPanZoomStack) {
describe("A plot pan-zoom stack", function () {
var panZoomStack,
initialOrigin,
initialDimensions,
otherOrigins,
otherDimensions;
// Shorthand for verifying getOrigin, getDimensions, and getPanZoom,
// which should always agree.
function verifyPanZoom(origin, dimensions) {
expect(panZoomStack.getOrigin()).toEqual(origin);
expect(panZoomStack.getDimensions()).toEqual(dimensions);
expect(panZoomStack.getPanZoom()).toEqual({
origin: origin,
dimensions: dimensions
});
}
beforeEach(function () {
initialOrigin = [4, 2];
initialDimensions = [600, 400];
otherOrigins = [[8, 6], [12, 9]];
otherDimensions = [[400, 300], [200, 300]];
panZoomStack =
new PlotPanZoomStack(initialOrigin, initialDimensions);
});
it("starts off reporting its initial values", function () {
verifyPanZoom(initialOrigin, initialDimensions);
});
it("allows origin/dimensions pairs to be pushed/popped", function () {
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
verifyPanZoom(otherOrigins[0], otherDimensions[0]);
panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
verifyPanZoom(otherOrigins[1], otherDimensions[1]);
panZoomStack.popPanZoom();
verifyPanZoom(otherOrigins[0], otherDimensions[0]);
panZoomStack.popPanZoom();
verifyPanZoom(initialOrigin, initialDimensions);
});
it("reports current stack depth", function () {
expect(panZoomStack.getDepth()).toEqual(1);
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
expect(panZoomStack.getDepth()).toEqual(2);
panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
expect(panZoomStack.getDepth()).toEqual(3);
});
it("allows base pan zoom to be restored", function () {
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
panZoomStack.clearPanZoom();
verifyPanZoom(initialOrigin, initialDimensions);
});
it("allows base pan zoom to be changed", function () {
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
panZoomStack.setBasePanZoom(otherOrigins[1], otherDimensions[1]);
// Should not have changed current top-of-stack
verifyPanZoom(otherOrigins[0], otherDimensions[0]);
// Clear the stack - should be at our new base pan-zoom state
panZoomStack.clearPanZoom();
verifyPanZoom(otherOrigins[1], otherDimensions[1]);
});
});
}
);

View File

@ -1,67 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotPosition"],
function (PlotPosition) {
describe("A plot position", function () {
var mockPanZoom,
testOrigin = [10, 20],
testDimensions = [800, 10];
beforeEach(function () {
mockPanZoom = jasmine.createSpyObj(
"panZoomStack",
["getPanZoom"]
);
mockPanZoom.getPanZoom.andReturn({
origin: testOrigin,
dimensions: testDimensions
});
});
it("transforms pixel coordinates to domain-range", function () {
var position = new PlotPosition(42, 450, 100, 1000, mockPanZoom);
// Domain: .42 * 800 + 10 = 346
// Range: .55 * 10 + 20 = 25.5
// Notably, y-axis is reversed between pixel space and range
expect(position.getPosition()).toEqual([346, 25.5]);
expect(position.getDomain()).toEqual(346);
expect(position.getRange()).toEqual(25.5);
});
it("treats a position as undefined if no pan-zoom state is present", function () {
var position;
mockPanZoom.getPanZoom.andReturn({});
position = new PlotPosition(1, 2, 100, 100, mockPanZoom);
expect(position.getDomain()).toBeUndefined();
expect(position.getRange()).toBeUndefined();
expect(position.getPosition()).toEqual([]);
});
});
}
);

View File

@ -1,93 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotPreparer"],
function (PlotPreparer) {
var START = 123456;
describe("A plot preparer", function () {
function makeMockData(scale) {
var mockData = jasmine.createSpyObj(
"data" + scale,
["getPointCount", "getDomainValue", "getRangeValue"]
);
mockData.getPointCount.andReturn(1000);
mockData.getDomainValue.andCallFake(function (i) {
return START + i * 1000;
});
mockData.getRangeValue.andCallFake(function (i) {
return Math.sin(i / 100) * scale;
});
return mockData;
}
it("fits to provided data sets", function () {
var datas = [1, 2, 3].map(makeMockData),
preparer = new PlotPreparer(datas);
expect(preparer.getDomainOffset()).toEqual(START);
expect(preparer.getOrigin()[0]).toBeCloseTo(START, 3);
expect(preparer.getOrigin()[1]).toBeCloseTo(-3, 3);
expect(preparer.getDimensions()[0]).toBeCloseTo(999000, 3);
expect(preparer.getDimensions()[1]).toBeCloseTo(6, 3);
});
it("looks up values using a specified domain and range", function () {
var datas = [makeMockData(1)],
preparer = new PlotPreparer(datas, "testDomain", "testRange");
expect(preparer).toBeDefined();
expect(datas[0].getDomainValue).toHaveBeenCalledWith(
jasmine.any(Number),
"testDomain"
);
expect(datas[0].getRangeValue).toHaveBeenCalledWith(
jasmine.any(Number),
"testRange"
);
});
it("provides a default range if data set is flat", function () {
var datas = [makeMockData(0)],
preparer = new PlotPreparer(datas);
expect(preparer.getDimensions[1]).not.toEqual(0);
});
it("provides buffers", function () {
var datas = [makeMockData(0)],
preparer = new PlotPreparer(datas);
expect(preparer.getBuffers()[0] instanceof Float32Array)
.toBeTruthy();
});
});
}
);

View File

@ -1,93 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../../src/elements/PlotSeriesWindow"],
function (PlotSeriesWindow) {
describe("A plot's window on a telemetry series", function () {
var mockSeries,
testSeries,
window;
beforeEach(function () {
testSeries = [
[0, 42],
[10, 1],
[20, 4],
[30, 9],
[40, 3]
];
mockSeries = jasmine.createSpyObj(
'series',
['getPointCount', 'getDomainValue', 'getRangeValue']
);
mockSeries.getPointCount.andCallFake(function () {
return testSeries.length;
});
mockSeries.getDomainValue.andCallFake(function (i) {
return testSeries[i][0];
});
mockSeries.getRangeValue.andCallFake(function (i) {
return testSeries[i][1];
});
window = new PlotSeriesWindow(
mockSeries,
"testDomain",
"testRange",
1,
testSeries.length
);
});
it("provides a window upon a data series", function () {
expect(window.getPointCount()).toEqual(4);
expect(window.getDomainValue(0)).toEqual(10);
expect(window.getRangeValue(0)).toEqual(1);
});
it("looks up using specific domain/range keys", function () {
window.getDomainValue(0);
window.getRangeValue(0);
expect(mockSeries.getDomainValue)
.toHaveBeenCalledWith(1, 'testDomain');
expect(mockSeries.getRangeValue)
.toHaveBeenCalledWith(1, 'testRange');
});
it("can be split into smaller windows", function () {
var windows = window.split();
expect(windows.length).toEqual(2);
expect(windows[0].getPointCount()).toEqual(2);
expect(windows[1].getPointCount()).toEqual(2);
expect(windows[0].getDomainValue(0)).toEqual(10);
expect(windows[1].getDomainValue(0)).toEqual(30);
expect(windows[0].getRangeValue(0)).toEqual(1);
expect(windows[1].getRangeValue(0)).toEqual(9);
});
});
}
);

View File

@ -1,71 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../../src/elements/PlotTelemetryFormatter"],
function (PlotTelemetryFormatter) {
describe("The PlotTelemetryFormatter", function () {
var mockFormatter,
formatter;
beforeEach(function () {
mockFormatter = jasmine.createSpyObj(
'telemetryFormatter',
['formatDomainValue', 'formatRangeValue']
);
formatter = new PlotTelemetryFormatter(mockFormatter);
});
describe("using domain & range format keys", function () {
var rangeFormat = "someRangeFormat",
domainFormat = "someDomainFormat";
beforeEach(function () {
formatter.setRangeFormat(rangeFormat);
formatter.setDomainFormat(domainFormat);
});
it("includes format in formatDomainValue calls", function () {
mockFormatter.formatDomainValue.andReturn("formatted!");
expect(formatter.formatDomainValue(12321))
.toEqual("formatted!");
expect(mockFormatter.formatDomainValue)
.toHaveBeenCalledWith(12321, domainFormat);
});
it("includes format in formatRangeValue calls for strings", function () {
mockFormatter.formatRangeValue.andReturn("formatted!");
expect(formatter.formatRangeValue('foo'))
.toEqual("formatted!");
expect(mockFormatter.formatRangeValue)
.toHaveBeenCalledWith('foo', rangeFormat);
});
it("formats numeric values with three fixed digits", function () {
expect(formatter.formatRangeValue(10)).toEqual("10.000");
});
});
});
}
);

View File

@ -1,73 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotTickGenerator"],
function (PlotTickGenerator) {
describe("A plot tick generator", function () {
var mockPanZoomStack,
mockFormatter,
generator;
beforeEach(function () {
mockPanZoomStack = jasmine.createSpyObj(
"panZoomStack",
["getPanZoom"]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
mockPanZoomStack.getPanZoom.andReturn({
origin: [0, 0],
dimensions: [100, 100]
});
generator =
new PlotTickGenerator(mockPanZoomStack, mockFormatter);
});
it("provides tick marks for range", function () {
expect(generator.generateRangeTicks(11).length).toEqual(11);
// Should have used range formatter
expect(mockFormatter.formatRangeValue).toHaveBeenCalled();
expect(mockFormatter.formatDomainValue).not.toHaveBeenCalled();
});
it("provides tick marks for domain", function () {
expect(generator.generateDomainTicks(11).length).toEqual(11);
// Should have used domain formatter
expect(mockFormatter.formatRangeValue).not.toHaveBeenCalled();
expect(mockFormatter.formatDomainValue).toHaveBeenCalled();
});
});
}
);

View File

@ -1,237 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/elements/PlotUpdater"],
function (PlotUpdater) {
describe("A plot updater", function () {
var mockSubscription,
testDomain,
testRange,
testDomainValues,
testRangeValues,
mockSeries,
updater;
function makeMockDomainObject(id) {
var mockDomainObject = jasmine.createSpyObj(
"object-" + id,
["getId", "getCapability", "getModel"]
);
mockDomainObject.getId.andReturn(id);
return mockDomainObject;
}
beforeEach(function () {
var ids = ['a', 'b', 'c'],
mockObjects = ids.map(makeMockDomainObject);
mockSubscription = jasmine.createSpyObj(
"subscription",
["getDomainValue", "getRangeValue", "getTelemetryObjects"]
);
mockSeries = jasmine.createSpyObj(
'series',
['getPointCount', 'getDomainValue', 'getRangeValue']
);
testDomain = "testDomain";
testRange = "testRange";
testDomainValues = { a: 3, b: 7, c: 13 };
testRangeValues = { a: 123, b: 456, c: 789 };
mockSubscription.getTelemetryObjects.andReturn(mockObjects);
mockSubscription.getDomainValue.andCallFake(function (mockObject) {
return testDomainValues[mockObject.getId()];
});
mockSubscription.getRangeValue.andCallFake(function (mockObject) {
return testRangeValues[mockObject.getId()];
});
updater = new PlotUpdater(
mockSubscription,
testDomain,
testRange,
1350 // Smaller max size for easier testing
);
});
it("provides one buffer per telemetry object", function () {
expect(updater.getLineBuffers().length).toEqual(3);
});
it("changes buffer count if telemetry object counts change", function () {
mockSubscription.getTelemetryObjects
.andReturn([makeMockDomainObject('a')]);
updater.update();
expect(updater.getLineBuffers().length).toEqual(1);
});
it("can handle delayed telemetry object availability", function () {
// The case can occur where getTelemetryObjects() returns an
// empty array - specifically, while objects are still being
// loaded. The updater needs to be able to cope with that
// case.
var tmp = mockSubscription.getTelemetryObjects();
mockSubscription.getTelemetryObjects.andReturn([]);
// Reinstantiate with the empty subscription
updater = new PlotUpdater(
mockSubscription,
testDomain,
testRange
);
// Should have 0 buffers for 0 objects
expect(updater.getLineBuffers().length).toEqual(0);
// Restore the three objects the test subscription would
// normally have.
mockSubscription.getTelemetryObjects.andReturn(tmp);
updater.update();
// Should have 3 buffers for 3 objects
expect(updater.getLineBuffers().length).toEqual(3);
});
it("accepts historical telemetry updates", function () {
var mockObject = mockSubscription.getTelemetryObjects()[0];
mockSeries.getPointCount.andReturn(3);
mockSeries.getDomainValue.andCallFake(function (i) {
return 1000 + i * 1000;
});
mockSeries.getRangeValue.andReturn(10);
// PlotLine & PlotLineBuffer are tested for most of the
// details here, so just check for some expected side
// effect; in this case, should see more points in the buffer
expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
updater.addHistorical(mockObject, mockSeries);
expect(updater.getLineBuffers()[0].getLength()).toEqual(4);
});
it("clears the domain offset if no objects are present", function () {
mockSubscription.getTelemetryObjects.andReturn([]);
updater.update();
expect(updater.getDomainOffset()).toBeUndefined();
});
it("handles empty historical telemetry updates", function () {
// General robustness check for when a series is empty
var mockObject = mockSubscription.getTelemetryObjects()[0];
mockSeries.getPointCount.andReturn(0);
mockSeries.getDomainValue.andCallFake(function (i) {
return 1000 + i * 1000;
});
mockSeries.getRangeValue.andReturn(10);
// PlotLine & PlotLineBuffer are tested for most of the
// details here, so just check for some expected side
// effect; in this case, should see more points in the buffer
expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
updater.addHistorical(mockObject, mockSeries);
expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
});
it("can initialize domain offset from historical telemetry", function () {
var tmp = mockSubscription.getTelemetryObjects();
mockSubscription.getTelemetryObjects.andReturn([]);
// Reinstantiate with the empty subscription
updater = new PlotUpdater(
mockSubscription,
testDomain,
testRange
);
// Restore subscription, provide some historical data
mockSubscription.getTelemetryObjects.andReturn(tmp);
mockSeries.getPointCount.andReturn(3);
mockSeries.getDomainValue.andCallFake(function (i) {
return 1000 + i * 1000;
});
mockSeries.getRangeValue.andReturn(10);
// PlotLine & PlotLineBuffer are tested for most of the
// details here, so just check for some expected side
// effect; in this case, should see more points in the buffer
expect(updater.getDomainOffset()).toBeUndefined();
updater.addHistorical(tmp[0], mockSeries);
expect(updater.getDomainOffset()).toBeDefined();
});
it("provides some margin for the range", function () {
var mockObject = mockSubscription.getTelemetryObjects()[0];
mockSeries.getPointCount.andReturn(3);
mockSeries.getDomainValue.andCallFake(function (i) {
return 1000 + i * 1000;
});
mockSeries.getRangeValue.andCallFake(function (i) {
return 10 + i; // 10, 20, 30
});
updater.addHistorical(mockObject, mockSeries);
expect(updater.getOrigin()[1]).toBeLessThan(10);
expect(updater.getDimensions()[1]).toBeGreaterThan(20);
});
describe("when no data is initially available", function () {
beforeEach(function () {
testDomainValues = {};
testRangeValues = {};
updater = new PlotUpdater(
mockSubscription,
testDomain,
testRange,
1350 // Smaller max size for easier testing
);
});
it("has no line data", function () {
// Either no lines, or empty lines are fine
expect(updater.getLineBuffers().map(function (lineBuffer) {
return lineBuffer.getLength();
}).reduce(function (a, b) {
return a + b;
}, 0)).toEqual(0);
});
it("determines initial domain bounds from first available data", function () {
testDomainValues.a = 123;
testRangeValues.a = 456;
updater.update();
expect(updater.getOrigin()[0]).toEqual(jasmine.any(Number));
expect(updater.getOrigin()[1]).toEqual(jasmine.any(Number));
expect(isNaN(updater.getOrigin()[0])).toBeFalsy();
expect(isNaN(updater.getOrigin()[1])).toBeFalsy();
});
});
});
}
);

View File

@ -1,87 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/modes/PlotModeOptions"],
function (PlotModeOptions) {
describe("Plot mode options", function () {
var mockDomainObject,
mockSubPlotFactory;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockSubPlotFactory = jasmine.createSpyObj(
"subPlotFactory",
["createSubPlot"]
);
});
it("offers only one option when one object is present", function () {
expect(
new PlotModeOptions([mockDomainObject], mockSubPlotFactory)
.getModeOptions().length
).toEqual(1);
});
it("offers two options when multiple objects are present", function () {
var objects = [
mockDomainObject,
mockDomainObject,
mockDomainObject,
mockDomainObject
];
expect(
new PlotModeOptions(objects, mockSubPlotFactory)
.getModeOptions().length
).toEqual(2);
});
it("allows modes to be changed", function () {
var plotModeOptions = new PlotModeOptions([
mockDomainObject,
mockDomainObject,
mockDomainObject,
mockDomainObject
], mockSubPlotFactory),
initialHandler = plotModeOptions.getModeHandler();
// Change the mode
plotModeOptions.getModeOptions().forEach(function (option) {
if (option !== plotModeOptions.getMode()) {
plotModeOptions.setMode(option);
}
});
// Mode should be different now
expect(plotModeOptions.getModeHandler())
.not.toBe(initialHandler);
});
});
}
);

View File

@ -1,184 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/modes/PlotOverlayMode"],
function (PlotOverlayMode) {
describe("Overlaid plot mode", function () {
var mockDomainObject,
mockSubPlotFactory,
mockPrepared,
testBuffers,
testDrawingObjects,
mode;
function createMockSubPlot() {
var mockSubPlot = jasmine.createSpyObj(
"subPlot",
[
"setDomainOffset",
"hover",
"startMarquee",
"endMarquee",
"getDrawingObject",
"update"
]
),
testDrawingObject = {};
// Track drawing objects in order of creation
testDrawingObjects.push(testDrawingObject);
mockSubPlot.getDrawingObject.andReturn(testDrawingObject);
return mockSubPlot;
}
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockSubPlotFactory = jasmine.createSpyObj(
"subPlotFactory",
["createSubPlot"]
);
// Prepared telemetry data
mockPrepared = jasmine.createSpyObj(
"prepared",
[
"getDomainOffset",
"getOrigin",
"getDimensions",
"getLineBuffers"
]
);
mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot);
// Act as if we have three buffers full of data
testBuffers = ['a', 'b', 'c'].map(function (id) {
var mockBuffer = jasmine.createSpyObj(
'buffer-' + id,
['getBuffer', 'getLength']
);
mockBuffer.getBuffer.andReturn([id]);
mockBuffer.getLength.andReturn(3);
return mockBuffer;
});
mockPrepared.getLineBuffers.andReturn(testBuffers);
mockPrepared.getDomainOffset.andReturn(1234);
mockPrepared.getOrigin.andReturn([10, 10]);
mockPrepared.getDimensions.andReturn([500, 500]);
// Clear out drawing objects
testDrawingObjects = [];
mode = new PlotOverlayMode([
mockDomainObject,
mockDomainObject,
mockDomainObject
], mockSubPlotFactory);
});
it("creates one sub-plot for all domain objects", function () {
expect(mode.getSubPlots().length).toEqual(1);
});
it("draws telemetry to subplots", function () {
// Verify precondition
mode.getSubPlots().forEach(function (subplot) {
// Either empty list or undefined is fine;
// just want to make sure there are no lines.
expect(subplot.getDrawingObject().lines || [])
.toEqual([]);
});
mode.plotTelemetry(mockPrepared);
// Should have one sub-plot with three lines
testDrawingObjects.forEach(function (testDrawingObject) {
// Either empty list or undefined is fine;
// just want to make sure there are no lines.
expect(testDrawingObject.lines.length)
.toEqual(3);
// Make sure the right buffer was drawn to the
// right subplot.
testDrawingObject.lines.forEach(function (line, j) {
expect(line.buffer).toEqual(testBuffers[j].getBuffer());
});
});
});
it("tracks zoomed state of subplots", function () {
// Should start out unzoomed
expect(mode.isZoomed()).toBeFalsy();
// Trigger some zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
// Second argument to the factory was pan-zoom stack
c.args[1].pushPanZoom([1, 2], [3, 4]);
});
// Should start out unzoomed
expect(mode.isZoomed()).toBeTruthy();
});
it("supports unzooming", function () {
// Trigger some zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
// Second argument to the factory was pan-zoom stack
c.args[1].pushPanZoom([1, 2], [3, 4]);
});
// Verify that we are indeed zoomed now
expect(mode.isZoomed()).toBeTruthy();
// Unzoom
mode.unzoom();
// Should no longer be zoomed
expect(mode.isZoomed()).toBeFalsy();
});
it("supports stepping back through zoom states", function () {
// Trigger some zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
// Second argument to the factory was pan-zoom stack
c.args[1].pushPanZoom([1, 2], [3, 4]);
});
// Step back the same number of zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function () {
// Should still be zoomed at start of each iteration
expect(mode.isZoomed()).toBeTruthy();
// Step back one of the zoom changes.
mode.stepBackPanZoom();
});
// Should no longer be zoomed
expect(mode.isZoomed()).toBeFalsy();
});
});
}
);

View File

@ -1,179 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/modes/PlotStackMode"],
function (PlotStackMode) {
describe("Stacked plot mode", function () {
var mockDomainObject,
mockSubPlotFactory,
mockPrepared,
testBuffers,
testDrawingObjects,
mode;
function createMockSubPlot() {
var mockSubPlot = jasmine.createSpyObj(
"subPlot",
[
"setDomainOffset",
"hover",
"startMarquee",
"endMarquee",
"getDrawingObject",
"update"
]
),
testDrawingObject = {};
// Track drawing objects in order of creation
testDrawingObjects.push(testDrawingObject);
mockSubPlot.getDrawingObject.andReturn(testDrawingObject);
return mockSubPlot;
}
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockSubPlotFactory = jasmine.createSpyObj(
"subPlotFactory",
["createSubPlot"]
);
// Prepared telemetry data
mockPrepared = jasmine.createSpyObj(
"prepared",
["getDomainOffset", "getOrigin", "getDimensions", "getLineBuffers"]
);
mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot);
// Act as if we have three buffers full of data
testBuffers = ['a', 'b', 'c'].map(function (id) {
var mockBuffer = jasmine.createSpyObj(
'buffer-' + id,
['getBuffer', 'getLength']
);
mockBuffer.getBuffer.andReturn([id]);
mockBuffer.getLength.andReturn(3);
return mockBuffer;
});
mockPrepared.getLineBuffers.andReturn(testBuffers);
mockPrepared.getDomainOffset.andReturn(1234);
mockPrepared.getOrigin.andReturn([10, 10]);
mockPrepared.getDimensions.andReturn([500, 500]);
// Objects that will be drawn to in sub-plots
testDrawingObjects = [];
mode = new PlotStackMode([
mockDomainObject,
mockDomainObject,
mockDomainObject
], mockSubPlotFactory);
});
it("creates one sub-plot per domain object", function () {
expect(mode.getSubPlots().length).toEqual(3);
});
it("draws telemetry to subplots", function () {
// Verify precondition
mode.getSubPlots().forEach(function (subplot) {
// Either empty list or undefined is fine;
// just want to make sure there are no lines.
expect(subplot.getDrawingObject().lines || [])
.toEqual([]);
});
mode.plotTelemetry(mockPrepared);
// Should all each have one line
testDrawingObjects.forEach(function (testDrawingObject, i) {
// Either empty list or undefined is fine;
// just want to make sure there are no lines.
expect(testDrawingObject.lines.length)
.toEqual(1);
// Make sure the right buffer was drawn to the
// right subplot.
expect(testDrawingObject.lines[0].buffer)
.toEqual(testBuffers[i].getBuffer());
});
});
it("tracks zoomed state of subplots", function () {
// Should start out unzoomed
expect(mode.isZoomed()).toBeFalsy();
// Trigger some zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
// Second argument to the factory was pan-zoom stack
c.args[1].pushPanZoom([1, 2], [3, 4]);
});
// Should start out unzoomed
expect(mode.isZoomed()).toBeTruthy();
});
it("supports unzooming", function () {
// Trigger some zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
// Second argument to the factory was pan-zoom stack
c.args[1].pushPanZoom([1, 2], [3, 4]);
});
// Verify that we are indeed zoomed now
expect(mode.isZoomed()).toBeTruthy();
// Unzoom
mode.unzoom();
// Should no longer be zoomed
expect(mode.isZoomed()).toBeFalsy();
});
it("supports stepping back through zoom states", function () {
// Trigger some zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
// Second argument to the factory was pan-zoom stack
c.args[1].pushPanZoom([1, 2], [3, 4]);
});
// Step back the same number of zoom changes
mockSubPlotFactory.createSubPlot.calls.forEach(function () {
// Should still be zoomed at start of each iteration
expect(mode.isZoomed()).toBeTruthy();
// Step back
mode.stepBackPanZoom();
});
// Should no longer be zoomed
expect(mode.isZoomed()).toBeFalsy();
});
});
}
);

View File

@ -1,123 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
define(
["../../src/policies/PlotViewPolicy"],
function (PlotViewPolicy) {
describe("Plot view policy", function () {
var testView,
mockDomainObject,
testAdaptedObject,
openmct,
telemetryMetadata,
policy;
beforeEach(function () {
testView = { key: "plot" };
testAdaptedObject = { telemetry: {} };
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['useCapability', 'hasCapability', 'getCapability']
);
mockDomainObject.useCapability.andReturn(testAdaptedObject);
openmct = {
telemetry: jasmine.createSpyObj('telemetryAPI', [
'getMetadata'
])
};
telemetryMetadata = jasmine.createSpyObj('telemetryMetadata', [
'valuesForHints'
]);
telemetryMetadata.valuesForHints.andReturn([]);
openmct.telemetry.getMetadata.andReturn(telemetryMetadata);
policy = new PlotViewPolicy(openmct);
});
it('fetches metadata from telem api', function () {
policy.allow(testView, mockDomainObject);
expect(mockDomainObject.useCapability)
.toHaveBeenCalledWith('adapter');
expect(openmct.telemetry.getMetadata)
.toHaveBeenCalledWith(testAdaptedObject);
expect(telemetryMetadata.valuesForHints)
.toHaveBeenCalledWith(['range']);
});
it('returns false if no ranges exist', function () {
telemetryMetadata.valuesForHints.andReturn([]);
expect(policy.allow(testView, mockDomainObject)).toBe(false);
});
it('returns true if any ranges exist', function () {
telemetryMetadata.valuesForHints.andReturn([{}]);
expect(policy.allow(testView, mockDomainObject)).toBe(true);
});
it('returns false if all ranges are strings', function () {
telemetryMetadata.valuesForHints.andReturn([{
format: 'string'
}, {
format: 'string'
}]);
expect(policy.allow(testView, mockDomainObject)).toBe(false);
});
it('returns true if only some ranges are strings', function () {
telemetryMetadata.valuesForHints.andReturn([{
format: 'string'
}, {}]);
expect(policy.allow(testView, mockDomainObject)).toBe(true);
});
it('returns true for telemetry delegators', function () {
delete testAdaptedObject.telemetry;
mockDomainObject.hasCapability.andCallFake(function (c) {
return c === 'delegation';
});
mockDomainObject.getCapability.andReturn(
jasmine.createSpyObj('delegation', [
'doesDelegateCapability'
])
);
mockDomainObject.getCapability('delegation')
.doesDelegateCapability.andCallFake(function (c) {
return c === 'telemetry';
});
expect(policy.allow(testView, mockDomainObject)).toBe(true);
expect(openmct.telemetry.getMetadata).not.toHaveBeenCalled();
});
it('returns true for non-telemetry non-delegators', function () {
delete testAdaptedObject.telemetry;
mockDomainObject.hasCapability.andReturn(false);
expect(policy.allow(testView, mockDomainObject)).toBe(false);
});
it("allows other views", function () {
testView.key = "somethingElse";
expect(policy.allow(testView, mockDomainObject)).toBe(true);
});
});
}
);

View File

@ -1,120 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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.
*****************************************************************************/
/**
* ExportImageServiceSpec. Created by hudsonfoo on 09/03/16.
*/
define(
["../../src/services/ExportImageService"],
function (ExportImageService) {
var mockQ,
mockDeferred,
mockPromise,
mockTimeout,
mockLog,
mockHtml2Canvas,
mockCanvas,
mockSaveAs,
mockFileReader,
mockExportTimeoutConstant,
testElement,
exportImageService;
describe("ExportImageService", function () {
beforeEach(function () {
mockDeferred = jasmine.createSpyObj(
"deferred",
["reject", "resolve"]
);
mockPromise = jasmine.createSpyObj(
"promise",
["then", "finally"]
);
mockPromise.then = function (callback) {
callback();
};
mockQ = {
"defer": function () {
return {
"resolve": mockDeferred.resolve,
"reject": mockDeferred.reject,
"promise": mockPromise
};
}
};
mockTimeout = function (fn, time) {
return {
"cancel": function () {}
};
};
mockLog = jasmine.createSpyObj(
"$log",
["warn"]
);
mockHtml2Canvas = jasmine.createSpy("html2canvas").andCallFake(function (element, opts) {
opts.onrendered(mockCanvas);
});
mockCanvas = jasmine.createSpyObj(
"canvas",
["toBlob"]
);
mockSaveAs = jasmine.createSpy("saveAs");
mockFileReader = jasmine.createSpyObj(
"FileReader",
["readAsDataURL", "onloadend"]
);
mockExportTimeoutConstant = 0;
testElement = {};
exportImageService = new ExportImageService(
mockQ,
mockTimeout,
mockLog,
mockExportTimeoutConstant,
mockHtml2Canvas,
mockSaveAs,
mockFileReader
);
});
it("runs html2canvas and tries to save a png", function () {
exportImageService.exportPNG(testElement, "plot.png");
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/png");
expect(mockDeferred.reject).not.toHaveBeenCalled();
expect(mockSaveAs).toHaveBeenCalled();
expect(mockPromise.finally).toHaveBeenCalled();
});
it("runs html2canvas and tries to save a jpg", function () {
exportImageService.exportJPG(testElement, "plot.png");
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/jpeg");
expect(mockDeferred.reject).not.toHaveBeenCalled();
expect(mockSaveAs).toHaveBeenCalled();
expect(mockPromise.finally).toHaveBeenCalled();
});
});
}
);

View File

@ -29,6 +29,7 @@ define([
"./src/controllers/TimelineTickController",
"./src/controllers/TimelineTableController",
"./src/controllers/TimelineGanttController",
"./src/controllers/TimelineTOIController",
"./src/controllers/ActivityModeValuesController",
"./src/capabilities/ActivityTimespanCapability",
"./src/capabilities/TimelineTimespanCapability",
@ -59,6 +60,7 @@ define([
TimelineTickController,
TimelineTableController,
TimelineGanttController,
TimelineTOIController,
ActivityModeValuesController,
ActivityTimespanCapability,
TimelineTimespanCapability,
@ -502,6 +504,15 @@ define([
"TIMELINE_MAXIMUM_OFFSCREEN"
]
},
{
"key": "TimelineTOIController",
"implementation": TimelineTOIController,
"depends": [
"openmct",
"timerService",
"$scope"
]
},
{
"key": "ActivityModeValuesController",
"implementation": ActivityModeValuesController,

View File

@ -29,6 +29,44 @@
}
}
}
// Follow Line
.l-follow-line {
// TODO: move before and after into l-timeline-gantt so those only render in that pane
pointer-events: none;
position: absolute;
top: 0; bottom: 0;
width: 1px;
z-index: 9; // Just below .l-hover-btns-holder
}
}
.l-timeline-gantt {
.l-follow-line {
$d: 0.8rem;
top: $interiorMargin;
&:before,
&:after {
content: '';
display: block;
height: $d;
width: $d;
position: absolute;
top: 0;
@include transform(translateX(-50%));
}
&:before {
// Icon blocker
width: 2 * $d;
}
&:after {
// Icon
font-size: $d;
line-height: $d;
text-align: center;
}
}
}
.s-timeline-gantt {
@ -108,10 +146,9 @@
}
.s-hover-btns-holder {
$bg: $timelineHeaderColorBg;
$bga: 1;
$l: 5%;
@include user-select(none);
@include background-image(linear-gradient(-90deg, rgba($bg, $bga), rgba($bg, $bga) 70%, rgba($bg, 0) 100%));
@include background-image(linear-gradient(-90deg, rgba($bg, 1), rgba($bg, 1) 70%, rgba($bg, 0) 100%));
.s-button {
height: 16px;
line-height: 16px;
@ -129,4 +166,27 @@
color: $timelineResourceGraphFg;
}
}
.s-follow-line {
background: rgba($timeControllerToiLineColor, 0.5);
}
.s-timeline-gantt {
.s-follow-line {
&:after {
// Icon
color: $timeControllerToiLineColor;
content: $glyph-icon-timer;
font-family: symbolsfont;
text-shadow: $shdwItemText;
}
&:before {
// Blocker
$bg: $timelineHeaderColorBg;
$l: 30%;
@include background-image(linear-gradient(90deg, rgba($bg, 0), rgba($bg, 1) $l, rgba($bg, 1) 100% - $l, rgba($bg, 0)));
}
}
}
}

View File

@ -75,6 +75,10 @@
}
}
&.l-timeline-gantt {
.abs.l-timeline-gantt-header-w {
overflow: hidden;
height: $timelineTopPaneHeaderH;
}
.l-swimlanes-holder {
@include scrollV(scroll);
bottom: $scrollbarTrackSize;

View File

@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/glyphs";
@import "../../../../commonUI/themes/espresso/res/sass/constants";
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
@import "constants";

View File

@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/glyphs";
@import "../../../../commonUI/themes/snow/res/sass/constants";
@import "../../../../commonUI/themes/snow/res/sass/mixins";
@import "constants";

Some files were not shown because too many files have changed in this diff Show More