Compare commits

..

159 Commits

Author SHA1 Message Date
Henry
b939eb2fbe Hard-code telemetry metadata 2017-10-30 16:53:40 -07:00
Henry
1afecdc82c Trying to support CreateAction 2017-10-30 14:16:06 -07:00
Henry
b9cda6985e Only add (Mean) to range values 2017-10-30 11:28:18 -07:00
Henry
401b7e3f19 Decorate PropertiesAction instead of persistence service to catch properties updates 2017-10-30 09:50:30 -07:00
Henry
c9e9845172 Telemetry mean function 2017-10-27 14:12:44 -07:00
Henry
abbba38eac Fix precision of displayed numberical telemetry to 2 DP 2017-10-27 09:14:39 -07:00
Victor Woeltjen
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
Charles Hacskaylo
e1566e448d [Frontend] Kill iframe borders dead!
Fixes #1776
(cherry picked from commit 1685364)
2017-10-13 17:11:27 -07:00
Charles Hacskaylo
d9aae0700c Merge branch 'master' into demo-2017.1 2017-10-13 10:28:06 -07:00
Charles Hacskaylo
5c29726cc0 Merge branch 'demo-2017.1' of https://github.com/nasa/openmct into demo-2017.1 2017-10-04 15:16:33 -07:00
Charles Hacskaylo
5c207c3fe0 Merge branch 'timeline-follow-1688' into demo-2017.1 2017-10-04 15:13:56 -07:00
Pete Richards
eba1dd1a4e [Plugins] Match args to define string 2017-10-04 14:07:04 -07:00
Charles Hacskaylo
570edc0dec Merge in fixes from no-frame-margin-1745 2017-10-04 10:28:29 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
aedbbbbf75 [Frontend] Remove border definition causing scrollbars
Fixes #1745
2017-10-04 09:44:36 -07:00
Charles Hacskaylo
3caaf00483 Merge branch 'summary-widgets' into demo-2017.1 2017-10-04 09:23:46 -07:00
Charles Hacskaylo
971eda4d88 [Frontend] Standardized font-size in widgets
Fixes #1668
2017-10-04 09:23:10 -07:00
Charles Hacskaylo
090d216517 [Frontend] Changed no-frame margin to 0 from 2px
Fixes #1745
2017-10-03 16:47:53 -07:00
Charles Hacskaylo
d42d7ae68d Merge remote-tracking branch 'origin/summary-widgets' into demo-2017.1 2017-10-03 14:28:32 -07:00
Deep Tailor
68e6e3c121 finalize changes for v1 and add/fix tests 2017-10-02 09:32:26 -07:00
Charles Hacskaylo
7e7f39db2d Add tooltip values for duplicate and delete buttons
Fixes #1668
2017-09-28 15:36:47 -07:00
Charles Hacskaylo
b6e0fca828 Standardize to sentence casing
Fixes #1668
2017-09-28 15:31:26 -07:00
Deep Tailor
ffc5896e5a Merge branch 'summary-widgets' of https://github.com/nasa/openmct into summary-widgets 2017-09-28 11:06:06 -07:00
Deep Tailor
fd6ebd152f fix and add appropriate tests 2017-09-28 11:05:42 -07:00
Charles Hacskaylo
7a5c1c0e1f Clean up commented code
Fixes #1668
2017-09-28 10:35:38 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
d73746b51b [Frontend] Fix for tooltip not appearing
Fixes #1668
2017-09-27 14:49:00 -07:00
Deep Tailor
2df54af019 Merge branch 'summary-widgets' of https://github.com/nasa/openmct into summary-widgets 2017-09-27 11:23:57 -07:00
Deep Tailor
586269f761 remove js option from rules 2017-09-27 11:23:47 -07:00
Charles Hacskaylo
e536ab34d7 [Frontend] Added l-flex-accordion class
Fixes #1668
2017-09-26 17:44:35 -07:00
Charles Hacskaylo
e15002dd72 [Frontend] Added Rules expand/contract
Fixes #1668
2017-09-26 17:43:53 -07:00
Deep Tailor
453cf3ad6a watch for object changes and update url and newTab properties 2017-09-26 14:18:09 -07:00
Charles Hacskaylo
5c46e48bde Merge lastest master, resolve conflicts in _menus.scss
Fixes #1668
2017-09-26 11:51:53 -07:00
Deep Tailor
868ea9362f make open in same tab as default option for url 2017-09-25 16:04:23 -07:00
Charles Hacskaylo
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
Deep Tailor
1658b17c56 add hyperlink functionality 2017-09-25 11:35:12 -07:00
Deep Tailor
39cf0528ca merge changes from charles 2017-09-25 10:04:44 -07:00
Deep Tailor
3d12f7312b checkstyle error fix 2017-09-25 10:04:25 -07:00
Charles Hacskaylo
22481fdc31 [Frontend] Set default widget colors and icon
Fixes #1668
2017-09-22 12:05:32 -07:00
Charles Hacskaylo
4a9d27dc79 [Frontend] Hover behaviors refined
Fixes #1668
2017-09-22 11:41:00 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
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
Charles Hacskaylo
a540a3573f [Front-end] WIP Styling for widget in layout
Fixes #1668
2017-09-21 15:30:12 -07:00
Charles Hacskaylo
4e7fe9082c Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling-2 2017-09-21 15:26:47 -07:00
Charles Hacskaylo
568141bf81 [Front-end] WIP Styling for widget in layout
Fixes #1668
2017-09-21 15:26:28 -07:00
Deep Tailor
ac3ea43fe5 hide equal to label until select field is available 2017-09-21 15:22:55 -07:00
Charles Hacskaylo
e922e8d504 [Front-end] Add summary widgets to hide-buttons-in-Layout list
Fixes #1668
2017-09-21 15:11:15 -07:00
Charles Hacskaylo
650a877d2a [Front-end] Inject icon directly into widget Label
Fixes #1668
Remove .widget-icon element;
2017-09-21 15:04:35 -07:00
Deep Tailor
1202109c59 Merge branch 'summary-widgets-styling-2' of https://github.com/nasa/openmct into summary-widgets 2017-09-21 14:53:27 -07:00
Deep Tailor
429d7bbd57 add hide/unhide to select.js 2017-09-21 14:53:06 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
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
Victor Woeltjen
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
Charles Hacskaylo
c5bd3da44a Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling-2 2017-09-21 11:39:04 -07:00
Deep Tailor
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
Charles Hacskaylo
e244a3e431 [Front-end] New null value selection strings
Fixes #1668
2017-09-21 11:23:26 -07:00
Charles Hacskaylo
c5d9fb6fd9 [Front-end] Hide/show "no-data" message element
Fixes #1688
2017-09-21 11:11:12 -07:00
Charles Hacskaylo
4c276ab422 [Front-end] Normalize font sizing in overlay
Fixes #1688
2017-09-21 10:26:47 -07:00
Charles Hacskaylo
bf321abae4 Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling-2 2017-09-21 10:09:57 -07:00
Charles Hacskaylo
7336968ef9 [Frontend] Add message markup and styling
Fixes #1688
Add to Summary Widgets UI;
2017-09-20 18:03:03 -07:00
Charles Hacskaylo
d60956948b [Frontend] Sanding to avoid CSS collision
Fixes #1688
2017-09-20 18:02:26 -07:00
Charles Hacskaylo
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
Deep Tailor
2632b8891a Merge latest styling changes from Charles 2017-09-20 14:56:05 -07:00
Deep Tailor
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
Victor Woeltjen
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
Charles Hacskaylo
be0291cf70 [Frontend] Removed t-condition from testDataItem
Fixes #1668
Fixes #1644
2017-09-19 17:51:16 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
ebeed2f236 [Frontend] Significant styling for test data area
Fixes #1668
Fixes #1644
2017-09-19 17:37:15 -07:00
Deep Tailor
337c26c019 fix failing tests 2017-09-18 13:12:23 -07:00
Deep Tailor
730f363f94 Merge branch 'summary-widgets-styling-2' of https://github.com/nasa/openmct into summary-widgets 2017-09-18 09:44:15 -07:00
Deep Tailor
ae30e6110b merge from styling 2017-09-18 09:44:07 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
5465ca92f9 [Frontend] Flex layout now working!
Fixes #1668
Fixes #1644
2017-09-15 17:33:28 -07:00
Charles Hacskaylo
e55ea41b0a [Frontend] Align selects
Fixes #1668
Fixes #1644
2017-09-15 17:02:20 -07:00
Charles Hacskaylo
8cfb3cc689 [Frontend] Merge latest master
Fixes #1668
Fixes #1644
2017-09-15 17:01:32 -07:00
Charles Hacskaylo
2d728a1362 [Frontend] WIP Styling continued
Fixes #1668
Markup tweaks
2017-09-15 16:44:11 -07:00
Charles Hacskaylo
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
Deep Tailor
de783d4286 fix circle-ci build errors 2017-09-14 14:11:02 -07:00
Victor Woeltjen
1e1a2443d5 [Timers] Remove unused variable to pass lint checks 2017-09-11 11:24:52 -07:00
Victor Woeltjen
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
Doubek-Kraft
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
Doubek-Kraft
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
Charles Hacskaylo
38f0f072bb [Merge] Ellipsize added to l-summary-widget
Fixes #1669
2017-08-25 14:19:43 -07:00
Charles Hacskaylo
e5e969665f [Merge] Merge latest master
Fixes #1669
Merge + resolve conflicts;
2017-08-25 14:11:34 -07:00
Doubek-Kraft
ffbb662c99 [Summary Widgets] Unit tests 2017-08-25 14:09:22 -07:00
Doubek-Kraft
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
Doubek-Kraft
c238def902 [Summary Widgets] Unit tests 2017-08-25 11:57:29 -07:00
Charles Hacskaylo
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
Doubek-Kraft
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
Doubek-Kraft
41ce3c04f7 [Summary Widgets] Unit Tests 2017-08-24 16:52:44 -07:00
Doubek-Kraft
fcf77f359f [Summary Widgets] Unit tests 2017-08-24 12:06:31 -07:00
Doubek-Kraft
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
Doubek-Kraft
216489d67f [Summary Widgets] Tests
Add unit tests
2017-08-23 17:01:14 -07:00
Doubek-Kraft
418a393b26 [Sumamry Widgets] Destroy
Implement destroy
2017-08-23 13:09:36 -07:00
Aaron Doubek-Kraft
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
Doubek-Kraft
ff3f2dccba [Summary Widget] Tests
Add and update unit tests

Fix style in markup

Begin implementing destroy
2017-08-22 16:51:00 -07:00
Doubek-Kraft
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
Doubek-Kraft
05b352cc36 [Summary Widgets] Documentation
Add JSDoc style comments
2017-08-21 17:05:41 -07:00
Doubek-Kraft
9735548999 [Summary Widgets] Add documentation 2017-08-18 16:22:23 -07:00
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Pete Richards
d0745b300b Allow views to be editable 2017-08-16 11:20:04 -07:00
Charles Hacskaylo
2a4e0a3081 [Frontend] Mods to custom selects
Fixes #1669
line-height, context arrow positioning and color
2017-08-16 10:50:08 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
7ef84cb50d [Front-end] Fix palette menu when no icon is selected
Fixes #1669
2017-08-15 15:51:32 -07:00
Charles Hacskaylo
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
Doubek-Kraft
568473b82f [Summary Widgets] Rule Reorders
Cleanup Widget DnD code and package it as a module
2017-08-14 16:24:38 -07:00
Doubek-Kraft
c61b074755 [Summary Widgets] Rule Reorders
Re-implement drag and drop with a 'sortable list' style interface
2017-08-14 14:21:37 -07:00
Doubek-Kraft
8ed66ab4ab [Summary Widgets] Rule Reorders
Implement drag and drop rule reorders using the native HTML5 API
2017-08-11 16:57:40 -07:00
Doubek-Kraft
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
Doubek-Kraft
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
Charles Hacskaylo
0c0ca6e6af [Frontend] Colors for palette defined
Fixes #1669
2017-08-09 16:00:33 -07:00
Doubek-Kraft
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
Doubek-Kraft
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
Charles Hacskaylo
8a8e3cc055 [Front-end] WIP colors for Summary Widget palettes
Fixes #1669
2017-08-08 16:11:52 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
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
Doubek-Kraft
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
Charles Hacskaylo
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
Charles Hacskaylo
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
Doubek-Kraft
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
Doubek-Kraft
7b62cf130c [Summary Widget] Add unit tests
Add tests for input classes

Fix code style errors
2017-08-07 17:07:21 -07:00
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
405bb55881 [Refactor] Cleanup old files 2017-08-01 13:51:20 -07:00
Doubek-Kraft
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
Doubek-Kraft
98a9d71a2e [Summary Widgets] Implementation for conditions
Support configuring and persisting multiple conditions per rule
2017-07-26 09:33:27 -07:00
Doubek-Kraft
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
Doubek-Kraft
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
Doubek-Kraft
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
Charles Hacskaylo
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
Doubek-Kraft
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
Doubek-Kraft
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
Charles Hacskaylo
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
Charles Hacskaylo
500733afb2 [Frontend] Add new icon-grippy
Fixes #1654
2017-07-21 10:25:18 -07:00
Charles Hacskaylo
2aa04b0a56 Merge remote-tracking branch 'origin/summary-widgets' into summary-widgets-styling 2017-07-21 10:04:23 -07:00
Doubek-Kraft
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
Charles Hacskaylo
8aeb365f5f [Frontend] WIP summary widget styling
Fixes #1654
Add condition duplicate and delete buttons
2017-07-21 09:56:57 -07:00
Charles Hacskaylo
827a28313d [Frontend] WIP summary widget styling
Fixes #1654
Fixes and tweaks post-merge
2017-07-21 09:48:54 -07:00
Doubek-Kraft
c83de8aad2 [Summary Widgets] Minor UI implementation 2017-07-21 09:24:27 -07:00
Charles Hacskaylo
b55f43b8df [Frontend] WIP summary widget styling
Fixes #1654
Merge latest from summary-widgets branch
2017-07-21 09:15:19 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
a103b4dbff [Frontend] WIP summary widget styling
Fixes #1654
Significant mods to markup and SCSS;
2017-07-20 17:56:57 -07:00
Charles Hacskaylo
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
Charles Hacskaylo
597327f138 [Frontend] WIP summary widget styling
Fixes #1654
New SCSS for widgets; markup mods in progress
2017-07-20 14:09:17 -07:00
Charles Hacskaylo
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
Doubek-Kraft
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
Charles Hacskaylo
c73f7259c2 [Frontend] WIP summary widget styling
Fixes #1654
Markup cleanups
2017-07-19 15:38:44 -07:00
Charles Hacskaylo
4c9235ba10 [Frontend] Apply new summary-widget glyph
Fixes #1654
2017-07-19 15:01:04 -07:00
Charles Hacskaylo
55e2a77df8 [Frontend] Add new summary-widget glyph
Fixes #1654
Updated font files and icomoon json file;
2017-07-19 15:00:46 -07:00
Doubek-Kraft
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
Doubek-Kraft
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
Pete Richards
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
165 changed files with 2194 additions and 7107 deletions

View File

@@ -21,6 +21,5 @@
"shadow": "outer",
"strict": "implied",
"undef": true,
"unused": "vars",
"latedef": "nofunc"
"unused": "vars"
}

15
API.md
View File

@@ -879,21 +879,6 @@ openmct.install(openmct.plugins.CouchDB('http://localhost:9200'))
* `openmct.plugins.Espresso` and `openmct.plugins.Snow` are two different
themes (dark and light) available for Open MCT. Note that at least one
of these themes must be installed for Open MCT to appear correctly.
* `openmct.plugins.URLIndicatorPlugin` adds an indicator which shows the
availability of a URL with the following options:
- `url` : URL to indicate the status of
- `cssClass`: Icon to show in the status bar, defaults to `icon-database`, [list of all icons](https://nasa.github.io/openmct/style-guide/#/browse/styleguide:home?view=items)
- `interval`: Interval between checking the connection, defaults to `10000`
- `label` Name showing up as text in the status bar, defaults to url
```javascript
openmct.install(openmct.plugins.URLIndicatorPlugin({
url: 'http://google.com',
cssClass: 'check',
interval: 10000,
label: 'Google'
})
);
```
* `openmct.plugins.LocalStorage` provides persistence of user-created
objects in browser-local storage. This is particularly useful in
development environments.

View File

@@ -88,7 +88,7 @@ and [`gulp`](http://gulpjs.com/).
To build Open MCT for deployment:
`npm run prepare`
`npm run prepublish`
This will compile and minify JavaScript sources, as well as copy over assets.
The contents of the `dist` folder will contain a runnable Open MCT

View File

@@ -17,12 +17,12 @@
"screenfull": "^3.0.0",
"node-uuid": "^1.4.7",
"comma-separated-values": "^3.6.4",
"file-saver": "^1.3.3",
"zepto": "^1.1.6",
"FileSaver.js": "^0.0.2",
"zepto": "1.2.0",
"eventemitter3": "^1.2.0",
"lodash": "3.10.1",
"almond": "~0.3.2",
"html2canvas": "^0.4.1",
"moment-timezone": "^0.5.13"
}
}
}

View File

@@ -1,17 +1,15 @@
machine:
node:
version: 4.7.0
dependencies:
pre:
- npm install -g npm@latest
deployment:
production:
branch: master
commands:
- npm install canvas nomnoml
- ./build-docs.sh
- git fetch --unshallow
- git push git@heroku.com:openmctweb-demo.git $CIRCLE_SHA1:refs/heads/master
openmct-demo:
branch: live_demo
heroku:
appname: openmct-demo
openmctweb-staging-deux:
branch: mobile
heroku:
@@ -24,4 +22,4 @@ test:
general:
branches:
ignore:
- gh-pages
- gh-pages

View File

@@ -2283,7 +2283,7 @@ To install build dependencies (only needs to be run once):
To build:
`npm run prepare`
`npm run prepublish`
This will compile and minify JavaScript sources, as well as copy over assets.
The contents of the `dist` folder will contain a runnable Open MCT

View File

@@ -44,7 +44,9 @@ define([
message = message.data;
var callback = this.callbacks[message.id];
if (callback) {
callback(message);
if (callback(message)) {
delete this.callbacks[message.id];
}
}
};
@@ -70,7 +72,6 @@ define([
deferred.resolve = resolve;
deferred.reject = reject;
});
var messageId;
function callback(message) {
if (message.error) {
@@ -78,27 +79,33 @@ define([
} else {
deferred.resolve(message.data);
}
delete this.callbacks[messageId];
return true;
}
messageId = this.dispatch('request', request, callback.bind(this));
this.dispatch('request', request, callback);
return promise;
};
WorkerInterface.prototype.subscribe = function (request, cb) {
function callback(message) {
var isCancelled = false;
var callback = function (message) {
if (isCancelled) {
return true;
}
cb(message.data);
};
var messageId = this.dispatch('subscribe', request, callback);
var messageId = this.dispatch('subscribe', request, callback)
return function () {
isCancelled = true;
this.dispatch('unsubscribe', {
id: messageId
});
delete this.callbacks[messageId];
}.bind(this);
};

View File

@@ -22,8 +22,6 @@
/*global require,__dirname*/
require("v8-compile-cache");
var gulp = require('gulp'),
sourcemaps = require('gulp-sourcemaps'),
path = require('path'),
@@ -179,4 +177,4 @@ gulp.task('install', [ 'assets', 'scripts' ]);
gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]);
gulp.task('build', [ 'verify', 'install' ]);
gulp.task('build', [ 'verify', 'install' ]);

View File

@@ -43,9 +43,7 @@
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"
}));
openmct.install(openmct.plugins.TelemetryMean());
openmct.install(openmct.plugins.Conductor({
menuOptions: [
{

View File

@@ -36,8 +36,6 @@ module.exports = function(config) {
files: [
{pattern: 'bower_components/**/*.js', included: false},
{pattern: 'node_modules/d3-*/**/*.js', included: false},
{pattern: 'node_modules/vue/**/*.js', included: false},
{pattern: 'node_modules/@cristian77/**/*.js', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'example/**/*.html', included: false},
{pattern: 'example/**/*.js', included: false},

View File

@@ -33,11 +33,10 @@ requirejs.config({
"moment": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
"saveAs": "bower_components/file-saver/FileSaver.min",
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
"screenfull": "bower_components/screenfull/dist/screenfull.min",
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",
"vue": "node_modules/vue/dist/vue.min",
"zepto": "bower_components/zepto/zepto.min",
"lodash": "bower_components/lodash/lodash",
"d3-selection": "node_modules/d3-selection/build/d3-selection.min",
@@ -49,8 +48,7 @@ requirejs.config({
"d3-format": "node_modules/d3-format/build/d3-format.min",
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
"d3-time": "node_modules/d3-time/build/d3-time.min",
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
"painterro": "node_modules/@cristian77/painterro/build/painterro.min"
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min"
},
"shim": {
"angular": {
@@ -68,12 +66,6 @@ requirejs.config({
"moment-duration-format": {
"deps": ["moment"]
},
"painterro": {
"exports": "Painterro"
},
"saveAs": {
"exports": "saveAs"
},
"screenfull": {
"exports": "screenfull"
},

View File

@@ -3,7 +3,6 @@
"version": "0.12.1-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"@cristian77/painterro": "^0.2.48",
"d3-array": "^1.0.2",
"d3-axis": "^1.0.4",
"d3-collection": "^1.0.2",
@@ -16,8 +15,7 @@
"d3-time-format": "^2.0.3",
"express": "^4.13.1",
"minimist": "^1.1.1",
"request": "^2.69.0",
"vue": "^2.5.6"
"request": "^2.69.0"
},
"devDependencies": {
"bower": "^1.7.7",
@@ -51,8 +49,7 @@
"moment": "^2.11.1",
"node-bourbon": "^4.2.3",
"requirejs": "2.1.x",
"split": "^1.0.0",
"v8-compile-cache": "^1.1.0"
"split": "^1.0.0"
},
"scripts": {
"start": "node app.js",
@@ -62,7 +59,7 @@
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
"docs": "npm run jsdoc ; npm run otherdoc",
"prepare": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install"
"prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install"
},
"repository": {
"type": "git",

View File

@@ -57,12 +57,7 @@
</div>
<mct-representation key="representation.selected.key"
mct-object="representation.selected.key && domainObject"
class="abs flex-elem grows object-holder-main scroll"
mct-selectable="{
item: domainObject.useCapability('adapter'),
oldItem: domainObject
}"
mct-init-select>
class="abs flex-elem grows object-holder-main scroll">
</mct-representation>
</div>
</div>

View File

@@ -19,21 +19,12 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div ng-controller="InspectorController as controller">
<div ng-controller="InspectorController">
<div ng-repeat="region in regions">
<mct-representation
key="'object-properties'"
mct-object="controller.selectedItem()"
key="region.content.key"
mct-object="domainObject"
ng-model="ngModel">
</mct-representation>
<div ng-if="!controller.hasProviderView()">
<mct-representation
key="inspectorKey"
mct-object="controller.selectedItem()"
ng-model="ngModel">
</mct-representation>
</div>
<div class='inspector-provider-view'>
</div>
</div>
</div>

View File

@@ -38,6 +38,8 @@
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item">
</mct-representation>
</span>
@@ -49,6 +51,8 @@
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item">
</mct-representation>
</span>

View File

@@ -42,37 +42,23 @@ define(
* @param event the mouse event
*/
ObjectHeaderController.prototype.updateName = function (event) {
if (!event || !event.currentTarget) {
return;
}
if (event && (event.type === 'blur' || event.which === 13)) {
var name = event.currentTarget.innerHTML;
if (event.type === 'blur') {
this.updateModel(event);
} else if (event.which === 13) {
this.updateModel(event);
event.currentTarget.blur();
window.getSelection().removeAllRanges();
}
};
if (name.length === 0) {
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
event.currentTarget.innerHTML = name;
}
/**
* Updates the model.
*
* @param event the mouse event
* @param private
*/
ObjectHeaderController.prototype.updateModel = function (event) {
var name = event.currentTarget.textContent.replace(/\n/g, ' ');
if (name !== this.$scope.domainObject.model.name) {
this.domainObject.getCapability('mutation').mutate(function (model) {
model.name = name;
});
}
if (name.length === 0) {
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
event.currentTarget.textContent = name;
}
if (name !== this.domainObject.getModel().name) {
this.domainObject.getCapability('mutation').mutate(function (model) {
model.name = name;
});
if (event.which === 13) {
event.currentTarget.blur();
}
}
};

View File

@@ -32,7 +32,6 @@ define(
mockTypeCapability,
mockEvent,
mockCurrentTarget,
model,
controller;
beforeEach(function () {
@@ -48,11 +47,8 @@ define(
type: mockTypeCapability
};
model = {
name: "Test name"
};
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "getModel"]);
mockDomainObject.getModel.andReturn(model);
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "model"]);
mockDomainObject.model = {name: "Test name"};
mockDomainObject.getCapability.andCallFake(function (key) {
return mockCapabilities[key];
});
@@ -61,7 +57,7 @@ define(
domainObject: mockDomainObject
};
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "textContent"]);
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "innerHTML"]);
mockCurrentTarget.blur.andReturn(mockCurrentTarget);
mockEvent = {
@@ -75,7 +71,7 @@ define(
it("updates the model with new name on blur", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "New name";
mockCurrentTarget.innerHTML = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
@@ -83,23 +79,23 @@ define(
it("updates the model with a default for blank names", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "";
mockCurrentTarget.innerHTML = "";
controller.updateName(mockEvent);
expect(mockCurrentTarget.textContent.length).not.toEqual(0);
expect(mockCurrentTarget.innerHTML.length).not.toEqual(0);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
});
it("does not update the model if the same name", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = mockDomainObject.getModel().name;
mockCurrentTarget.innerHTML = mockDomainObject.model.name;
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
});
it("updates the model on enter keypress event only", function () {
mockCurrentTarget.textContent = "New name";
mockCurrentTarget.innerHTML = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
@@ -109,13 +105,12 @@ define(
expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function));
mockMutationCapability.mutate.mostRecentCall.args[0](model);
mockMutationCapability.mutate.mostRecentCall.args[0](mockDomainObject.model);
expect(mockDomainObject.getModel().name).toBe("New name");
expect(mockDomainObject.model.name).toBe("New name");
});
it("blurs the field on enter key press", function () {
mockCurrentTarget.textContent = "New name";
mockEvent.which = 13;
controller.updateName(mockEvent);

View File

@@ -121,8 +121,7 @@ define([
"key": "ElementsController",
"implementation": ElementsController,
"depends": [
"$scope",
"openmct"
"$scope"
]
},
{
@@ -300,6 +299,9 @@ define([
{
"key": "edit-elements",
"template": elementsTemplate,
"uses": [
"composition"
],
"gestures": [
"drop"
]
@@ -383,10 +385,7 @@ define([
]
},
{
"implementation": EditToolbarRepresenter,
"depends": [
"openmct"
]
"implementation": EditToolbarRepresenter
}
],
"constants": [

View File

@@ -61,12 +61,7 @@
<mct-representation key="representation.selected.key"
mct-object="representation.selected.key && domainObject"
class="abs flex-elem grows object-holder-main scroll"
toolbar="toolbar"
mct-selectable="{
item: domainObject.useCapability('adapter'),
oldItem: domainObject
}"
mct-init-select>
toolbar="toolbar">
</mct-representation>
</div><!--/ l-object-wrapper-inner -->
</div>

View File

@@ -25,7 +25,7 @@
ng-model="filterBy">
</mct-include>
<div class="flex-elem grows vscroll">
<ul class="tree" ng-if="composition.length > 0">
<ul class="tree">
<li ng-repeat="containedObject in composition | filter:searchElements">
<span class="tree-item">
<mct-representation
@@ -36,6 +36,5 @@
</span>
</li>
</ul>
<div ng-if="composition.length === 0">No contained elements</div>
</div>
</div>

View File

@@ -101,15 +101,10 @@ define(
*/
EditorCapability.prototype.finish = function () {
var domainObject = this.domainObject;
if (this.transactionService.isActive()) {
return this.transactionService.cancel().then(function () {
domainObject.getCapability("status").set("editing", false);
return domainObject;
});
} else {
return Promise.resolve(domainObject);
}
return this.transactionService.cancel().then(function () {
domainObject.getCapability("status").set("editing", false);
return domainObject;
});
};
/**

View File

@@ -28,6 +28,16 @@ define(
[],
function () {
function isDirty(domainObject) {
var navigatedObject = domainObject,
editorCapability = navigatedObject &&
navigatedObject.getCapability("editor");
return editorCapability &&
editorCapability.isEditContextRoot() &&
editorCapability.dirty();
}
function cancelEditing(domainObject) {
var navigatedObject = domainObject,
editorCapability = navigatedObject &&
@@ -49,7 +59,10 @@ define(
var removeCheck = navigationService
.checkBeforeNavigation(function () {
return "Continuing will cause the loss of any unsaved changes.";
if (isDirty(domainObject)) {
return "Continuing will cause the loss of any unsaved changes.";
}
return false;
});
$scope.$on('$destroy', function () {

View File

@@ -29,11 +29,7 @@ define(
*
* @constructor
*/
function ElementsController($scope, openmct) {
this.scope = $scope;
this.scope.composition = [];
var self = this;
function ElementsController($scope) {
function filterBy(text) {
if (typeof text === 'undefined') {
return $scope.searchText;
@@ -51,45 +47,10 @@ define(
}
}
function setSelection(selection) {
self.scope.selection = selection;
self.refreshComposition(selection);
}
$scope.filterBy = filterBy;
$scope.searchElements = searchElements;
openmct.selection.on('change', setSelection);
setSelection(openmct.selection.get());
$scope.$on("$destroy", function () {
openmct.selection.off("change", setSelection);
});
}
/**
* Gets the composition for the selected object and populates the scope with it.
*
* @param selection the selection object
* @private
*/
ElementsController.prototype.refreshComposition = function (selection) {
if (!selection[0]) {
return;
}
var selected = selection[0].context.oldItem;
var selectedObjectComposition = selected && selected.useCapability('composition');
if (selectedObjectComposition) {
selectedObjectComposition.then(function (composition) {
this.scope.composition = composition;
}.bind(this));
} else {
this.scope.composition = [];
}
};
return ElementsController;
}
);

View File

@@ -80,6 +80,12 @@ define(
return closeEditor();
}
function resolveWith (object) {
return function () {
return object;
}
}
newModel.type = this.type.getKey();
newModel.location = this.parent.getId();
newObject = this.parent.useCapability('instantiation', newModel);

View File

@@ -38,7 +38,7 @@ define(
* @constructor
* @implements {Representer}
*/
function EditToolbarRepresenter(openmct, scope, element, attrs) {
function EditToolbarRepresenter(scope, element, attrs) {
var self = this;
// Mark changes as ready to persist
@@ -109,7 +109,6 @@ define(
this.updateSelection = updateSelection;
this.toolbar = undefined;
this.toolbarObject = {};
this.openmct = openmct;
// If this representation exposes a toolbar, set up watches
// to synchronize with it.
@@ -147,7 +146,7 @@ define(
// Expose the toolbar object to the parent scope
initialize(definition);
// Create a selection scope
this.setSelection(new EditToolbarSelection(this.openmct));
this.setSelection(new EditToolbarSelection());
// Initialize toolbar to an empty selection
this.updateSelection([]);
};

View File

@@ -38,24 +38,10 @@ define(
* @memberof platform/commonUI/edit
* @constructor
*/
function EditToolbarSelection(openmct) {
function EditToolbarSelection() {
this.selection = [{}];
this.selecting = false;
this.selectedObj = undefined;
openmct.selection.on('change', function (selection) {
var selected = selection[0];
if (selected && selected.context.toolbar) {
this.select(selected.context.toolbar);
} else {
this.deselect();
}
if (selected && selected.context.viewProxy) {
this.proxy(selected.context.viewProxy);
}
}.bind(this));
}
/**

View File

@@ -62,7 +62,6 @@ define(
);
mockTransactionService.commit.andReturn(fastPromise());
mockTransactionService.cancel.andReturn(fastPromise());
mockTransactionService.isActive = jasmine.createSpy('isActive');
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
@@ -142,7 +141,6 @@ define(
describe("finish", function () {
beforeEach(function () {
mockTransactionService.isActive.andReturn(true);
capability.edit();
capability.finish();
});
@@ -154,23 +152,6 @@ define(
});
});
describe("finish", function () {
beforeEach(function () {
mockTransactionService.isActive.andReturn(false);
capability.edit();
});
it("does not cancel transaction when transaction is not active", function () {
capability.finish();
expect(mockTransactionService.cancel).not.toHaveBeenCalled();
});
it("returns a promise", function () {
expect(capability.finish() instanceof Promise).toBe(true);
});
});
describe("dirty", function () {
var model = {};

View File

@@ -104,10 +104,10 @@ define(
mockEditorCapability.isEditContextRoot.andReturn(false);
mockEditorCapability.dirty.andReturn(false);
expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
expect(checkFn()).toBe(false);
mockEditorCapability.isEditContextRoot.andReturn(true);
expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
expect(checkFn()).toBe(false);
mockEditorCapability.dirty.andReturn(true);
expect(checkFn())

View File

@@ -27,23 +27,11 @@ define(
describe("The Elements Pane controller", function () {
var mockScope,
mockOpenMCT,
mockSelection,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ['$on']);
mockSelection = jasmine.createSpyObj("selection", [
'on',
'off',
'get'
]);
mockSelection.get.andReturn([]);
mockOpenMCT = {
selection: mockSelection
};
controller = new ElementsController(mockScope, mockOpenMCT);
mockScope = jasmine.createSpy("$scope");
controller = new ElementsController(mockScope);
});
function getModel(model) {

View File

@@ -29,9 +29,7 @@ define(
mockElement,
testAttrs,
mockUnwatch,
representer,
mockOpenMCT,
mockSelection;
representer;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
@@ -48,18 +46,7 @@ define(
mockScope.$parent.$watchCollection.andReturn(mockUnwatch);
mockSelection = jasmine.createSpyObj("selection", [
'on',
'off',
'get'
]);
mockSelection.get.andReturn([]);
mockOpenMCT = {
selection: mockSelection
};
representer = new EditToolbarRepresenter(
mockOpenMCT,
mockScope,
mockElement,
testAttrs

View File

@@ -28,25 +28,13 @@ define(
var testProxy,
testElement,
otherElement,
selection,
mockSelection,
mockOpenMCT;
selection;
beforeEach(function () {
testProxy = { someKey: "some value" };
testElement = { someOtherKey: "some other value" };
otherElement = { yetAnotherKey: 42 };
mockSelection = jasmine.createSpyObj("selection", [
// 'select',
'on',
'off',
'get'
]);
mockSelection.get.andReturn([]);
mockOpenMCT = {
selection: mockSelection
};
selection = new EditToolbarSelection(mockOpenMCT);
selection = new EditToolbarSelection();
selection.proxy(testProxy);
});

View File

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

View File

@@ -41,7 +41,6 @@ define([
"./src/controllers/BannerController",
"./src/directives/MCTContainer",
"./src/directives/MCTDrag",
"./src/directives/MCTSelectable",
"./src/directives/MCTClickElsewhere",
"./src/directives/MCTResize",
"./src/directives/MCTPopup",
@@ -91,7 +90,6 @@ define([
BannerController,
MCTContainer,
MCTDrag,
MCTSelectable,
MCTClickElsewhere,
MCTResize,
MCTPopup,
@@ -330,13 +328,6 @@ define([
"$document"
]
},
{
"key": "mctSelectable",
"implementation": MCTSelectable,
"depends": [
"openmct"
]
},
{
"key": "mctClickElsewhere",
"implementation": MCTClickElsewhere,

View File

@@ -25,7 +25,6 @@
}
.l-fixed-position-item {
border-width: 1px;
position: absolute;
&.s-not-selected {
opacity: 0.8;

View File

@@ -290,11 +290,6 @@
opacity: 1;
}
}
.l-rule-action-buttons-wrapper {
.t-delete {
margin-left: 10px;
}
}
.t-condition {
&:hover {
.l-condition-action-buttons-wrapper {

View File

@@ -80,32 +80,23 @@
// Editing Grids
.l-grid-holder {
display: block;
.l-grid {
&.l-grid-x { @include bgTicks($colorGridLines, 'x'); }
&.l-grid-y { @include bgTicks($colorGridLines, 'y'); }
}
}
// Display grid when selected or selection parent.
.s-selected .l-grid-holder,
.s-selected-parent .l-grid-holder {
display: block;
// Prevent nested frames from showing their grids
.t-frame-outer .l-grid-holder { display: none !important; }
// Prevent nested elements from showing s-hover-border
.t-frame-outer .s-hover-border {
border: none !important;
}
// Display in nested frames...
.t-frame-outer {
// ...when drilled in or selection parent...
&.s-drilled-in, &.s-selected-parent {
.l-grid-holder {
display: block;
}
.t-frame-outer:not(.s-drilled-in) .l-grid-holder {
display: none;
}
}
// ...but hide otherwise.
.l-grid-holder {
display: none;
}
// Prevent nested frames from being selectable until we have proper sub-object editing
.t-frame-outer .t-frame-outer {
pointer-events: none;
}
}

View File

@@ -26,10 +26,12 @@
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
&:not(.no-frame) {
background: $colorBodyBg;
border-color: $bc;
border: 1px solid $bc;
&:hover {
border-color: lighten($bc, 10%);
}
}
}
.object-browse-bar {
font-size: 0.75em;
height: $ohH;
@@ -43,7 +45,9 @@
&.t-object-type-timer,
&.t-object-type-clock,
&.t-object-type-hyperlink,
&.t-object-type-summary-widget {
&.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.
@@ -90,9 +94,9 @@
&.no-frame {
background: transparent !important;
border-color: transparent;
border: none !important;
.object-browse-bar .right {
$m: 0;
$m: 0; // $interiorMarginSm;
background: rgba(black, 0.3);
border-radius: $basicCr;
padding: $interiorMarginSm;
@@ -113,7 +117,6 @@
display: none;
}
> .object-holder.abs {
overflow: hidden;
top: 0 !important;
}
}

View File

@@ -20,52 +20,35 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.s-hover-border {
border: 1px solid transparent;
&:hover {
border-color: rgba($colorSelectableSelectedPrimary, 0.5) !important;
}
border: none;
}
.s-status-editing {
// Limit to editing mode
$o: 0.5;
$oHover: 0.8;
$bc: $colorSelectableSelectedPrimary;
// Limit to editing mode until we have sub-object selection
.s-hover-border {
// Show a border by default so user can see object bounds and empty objects
border-color: rgba($bc, $o) !important;
border-style: dotted !important;
border: 1px dotted rgba($colorSelectableSelectedPrimary, 0.3) !important;
&:hover {
border-color: rgba($bc, $oHover) !important;
}
&.t-object-type-layout {
border-style: dashed !important;
border-color: rgba($colorSelectableSelectedPrimary, 0.7) !important;
}
}
.s-selected {
&.s-moveable {
&:not(.s-drilled-in) {
cursor: move;
.s-selected > .s-hover-border,
.s-selected.s-hover-border {
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
border-color: $colorSelectableSelectedPrimary !important;
@include boxShdwLarge();
// Show edit-corners if you got 'em
.edit-corner {
display: block;
&:hover {
background-color: rgba($colorKey, 1);
}
}
}
}
.s-selected > .s-hover-border,
.s-selected.s-hover-border {
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
border-color: $colorSelectableSelectedPrimary !important;
@include boxShdwLarge();
// Show edit-corners if you got 'em
.edit-corner {
display: block;
&:hover {
background-color: rgba($colorKey, 1);
}
.s-selected > .s-moveable,
.s-selected.s-moveable {
cursor: move;
}
}
}

View File

@@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="DateTimeFieldController">
<input type="text" autocorrect="off" spellcheck="false"
<input type="text"
ng-model="textValue"
ng-blur="restoreTextValue(); ngBlur()"
ng-mouseup="ngMouseup()"

View File

@@ -40,7 +40,7 @@ define(
// Gets an array of the contextual parents/ancestors of the selected object
function getContextualPath() {
var currentObj = $scope.domainObject,
var currentObj = $scope.ngModel.selectedObject,
currentParent,
parents = [];
@@ -68,7 +68,7 @@ define(
// If this the the initial call of this recursive function
if (!current) {
current = $scope.domainObject;
current = $scope.ngModel.selectedObject;
$scope.primaryParents = [];
}
@@ -87,16 +87,16 @@ define(
// Gets the metadata for the selected object
function getMetadata() {
$scope.metadata = $scope.domainObject &&
$scope.domainObject.hasCapability('metadata') &&
$scope.domainObject.useCapability('metadata');
$scope.metadata = $scope.ngModel.selectedObject &&
$scope.ngModel.selectedObject.hasCapability('metadata') &&
$scope.ngModel.selectedObject.useCapability('metadata');
}
// Set scope variables when the selected object changes
$scope.$watch('domainObject', function () {
$scope.isLink = $scope.domainObject &&
$scope.domainObject.hasCapability('location') &&
$scope.domainObject.getCapability('location').isLink();
$scope.$watch('ngModel.selectedObject', function () {
$scope.isLink = $scope.ngModel.selectedObject &&
$scope.ngModel.selectedObject.hasCapability('location') &&
$scope.ngModel.selectedObject.getCapability('location').isLink();
if ($scope.isLink) {
getPrimaryPath();
@@ -109,7 +109,7 @@ define(
getMetadata();
});
var mutation = $scope.domainObject.getCapability('mutation');
var mutation = $scope.ngModel.selectedObject.getCapability('mutation');
var unlisten = mutation.listen(getMetadata);
$scope.$on('$destroy', unlisten);
}

View File

@@ -1,60 +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 mct-selectable directive allows selection functionality
* (click) to be attached to specific elements.
*
* @memberof platform/commonUI/general
* @constructor
*/
function MCTSelectable(openmct) {
// Link; install event handlers.
function link(scope, element, attrs) {
var removeSelectable = openmct.selection.selectable(
element[0],
scope.$eval(attrs.mctSelectable),
attrs.hasOwnProperty('mctInitSelect') && scope.$eval(attrs.mctInitSelect) !== false
);
scope.$on("$destroy", function () {
removeSelectable();
});
}
return {
// mct-selectable only makes sense as an attribute
restrict: "A",
// Link function, to install event handlers
link: link
};
}
return MCTSelectable;
}
);

View File

@@ -41,6 +41,16 @@ define(
"$scope",
["$watch", "$on"]
);
mockScope.ngModel = {};
mockScope.ngModel.selectedObject = {
getCapability: function () {
return {
listen: function () {
return true;
}
};
}
};
mockObjectService = jasmine.createSpyObj(
"objectService",
@@ -67,27 +77,22 @@ define(
"location capability",
["isLink"]
);
mockDomainObject.getCapability.andCallFake(function (param) {
if (param === 'location') {
return mockLocationCapability;
} else if (param === 'context') {
return mockContextCapability;
} else if (param === 'mutation') {
return {
listen: function () {
return true;
}
};
}
});
mockScope.domainObject = mockDomainObject;
controller = new ObjectInspectorController(mockScope, mockObjectService);
// Change the selected object to trigger the watch call
mockScope.ngModel.selectedObject = mockDomainObject;
});
it("watches for changes to the selected object", function () {
expect(mockScope.$watch).toHaveBeenCalledWith('domainObject', jasmine.any(Function));
expect(mockScope.$watch).toHaveBeenCalledWith('ngModel.selectedObject', jasmine.any(Function));
});
it("looks for contextual parent objects", function () {

View File

@@ -65,10 +65,6 @@ define(
options = Object.create(OPTIONS);
options.marginX = -bubbleSpaceLR;
// prevent bubble from appearing right under pointer,
// which causes hover callback to be called multiple times
options.offsetX = 1;
// On a phone, bubble takes up more screen real estate,
// so position it differently (toward the bottom)
if (this.agentService.isPhone()) {

View File

@@ -38,8 +38,7 @@ define([
"implementation": InspectorController,
"depends": [
"$scope",
"openmct",
"$document"
"policyService"
]
}
],

View File

@@ -21,73 +21,44 @@
*****************************************************************************/
define(
[],
function () {
['../../browse/src/InspectorRegion'],
function (InspectorRegion) {
/**
* The InspectorController listens for the selection changes and adds the selection
* object to the scope.
* The InspectorController adds region data for a domain object's type
* to the scope.
*
* @constructor
*/
function InspectorController($scope, openmct, $document) {
var self = this;
self.$scope = $scope;
function InspectorController($scope, policyService) {
var domainObject = $scope.domainObject,
typeCapability = domainObject.getCapability('type'),
statusListener;
/**
* Callback handler for the selection change event.
* Adds the selection object to the scope. If the selected item has an inspector view,
* it puts the key in the scope. If provider view exists, it shows the view.
* Filters region parts to only those allowed by region policies
* @param regions
* @returns {{}}
*/
function setSelection(selection) {
if (selection[0]) {
var view = openmct.inspectorViews.get(selection);
var container = $document[0].querySelectorAll('.inspector-provider-view')[0];
container.innerHTML = "";
if (view) {
self.providerView = true;
view.show(container);
} else {
self.providerView = false;
var selectedItem = selection[0].context.oldItem;
if (selectedItem) {
$scope.inspectorKey = selectedItem.getCapability("type").typeDef.inspector;
}
}
}
self.$scope.selection = selection;
function filterRegions(inspector) {
//Dupe so we're not modifying the type definition.
return inspector.regions && inspector.regions.filter(function (region) {
return policyService.allow('region', region, domainObject);
});
}
openmct.selection.on("change", setSelection);
setSelection(openmct.selection.get());
function setRegions() {
$scope.regions = filterRegions(typeCapability.getDefinition().inspector || new InspectorRegion());
}
statusListener = domainObject.getCapability("status").listen(setRegions);
$scope.$on("$destroy", function () {
openmct.selection.off("change", setSelection);
statusListener();
});
setRegions();
}
/**
* Gets the selected item.
*
* @returns a domain object
*/
InspectorController.prototype.selectedItem = function () {
return this.$scope.selection[0].context.oldItem;
};
/**
* Checks if a provider view exists.
*
* @returns 'true' if provider view exists, 'false' otherwise
*/
InspectorController.prototype.hasProviderView = function () {
return this.providerView;
};
return InspectorController;
}
);

View File

@@ -27,93 +27,82 @@ define(
describe("The inspector controller ", function () {
var mockScope,
mockDomainObject,
mockOpenMCT,
mockSelection,
mockInspectorViews,
mockTypeDef,
controller,
container,
$document = [],
selectable = [];
mockTypeCapability,
mockTypeDefinition,
mockPolicyService,
mockStatusCapability,
capabilities = {},
controller;
beforeEach(function () {
mockTypeDef = {
typeDef: {
inspector: "some-key"
}
mockTypeDefinition = {
inspector:
{
'regions': [
{'name': 'Part One'},
{'name': 'Part Two'}
]
}
};
mockTypeCapability = jasmine.createSpyObj('typeCapability', [
'getDefinition'
]);
mockTypeCapability.getDefinition.andReturn(mockTypeDefinition);
capabilities.type = mockTypeCapability;
mockStatusCapability = jasmine.createSpyObj('statusCapability', [
'listen'
]);
capabilities.status = mockStatusCapability;
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getCapability'
]);
mockDomainObject.getCapability.andReturn(mockTypeDef);
mockDomainObject.getCapability.andCallFake(function (name) {
return capabilities[name];
});
mockPolicyService = jasmine.createSpyObj('policyService', [
'allow'
]);
mockScope = jasmine.createSpyObj('$scope',
['$on', 'selection']
['$on']
);
selectable[0] = {
context: {
oldItem: mockDomainObject
}
};
mockSelection = jasmine.createSpyObj("selection", [
'on',
'off',
'get'
]);
mockSelection.get.andReturn(selectable);
mockInspectorViews = jasmine.createSpyObj('inspectorViews', ['get']);
mockOpenMCT = {
selection: mockSelection,
inspectorViews: mockInspectorViews
};
container = jasmine.createSpy('container', ['innerHTML']);
$document[0] = jasmine.createSpyObj("$document", ['querySelectorAll']);
$document[0].querySelectorAll.andReturn([container]);
controller = new InspectorController(mockScope, mockOpenMCT, $document);
mockScope.domainObject = mockDomainObject;
});
it("listens for selection change event", function () {
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
expect(controller.selectedItem()).toEqual(mockDomainObject);
var mockItem = jasmine.createSpyObj('domainObject', [
'getCapability'
]);
mockItem.getCapability.andReturn(mockTypeDef);
selectable[0].context.oldItem = mockItem;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
expect(controller.selectedItem()).toEqual(mockItem);
it("filters out regions disallowed by region policy", function () {
mockPolicyService.allow.andReturn(false);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.regions.length).toBe(0);
});
it("cleans up on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.calls[0].args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
it("does not filter out regions allowed by region policy", function () {
mockPolicyService.allow.andReturn(true);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.regions.length).toBe(2);
});
it("adds selection object to scope", function () {
expect(mockScope.selection).toEqual(selectable);
expect(controller.selectedItem()).toEqual(mockDomainObject);
it("Responds to status changes", function () {
mockPolicyService.allow.andReturn(true);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.regions.length).toBe(2);
expect(mockStatusCapability.listen).toHaveBeenCalled();
mockPolicyService.allow.andReturn(false);
mockStatusCapability.listen.mostRecentCall.args[0]();
expect(mockScope.regions.length).toBe(0);
});
it("Unregisters status listener", function () {
var mockListener = jasmine.createSpy('listener');
mockStatusCapability.listen.andReturn(mockListener);
controller = new InspectorController(mockScope, mockPolicyService);
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", jasmine.any(Function));
mockScope.$on.mostRecentCall.args[1]();
expect(mockListener).toHaveBeenCalled();
});
});
}

View File

@@ -0,0 +1,54 @@
define([
'text!./res/templates/autoflow-tabular.html',
'./src/AutoflowTabularController',
'./src/MCTAutoflowTable'
], function (
autoflowTabularTemplate,
AutoflowTabularController,
MCTAutoflowTable
) {
return function (options) {
return function (openmct) {
openmct.legacyRegistry.register("platform/features/autoflow", {
"name": "WARP Telemetry Adapter",
"description": "Retrieves telemetry from the WARP Server and provides related types and views.",
"resources": "res",
"extensions": {
"views": [
{
"key": "autoflow",
"name": "Autoflow Tabular",
"cssClass": "icon-packet",
"description": "A tabular view of packet contents.",
"template": autoflowTabularTemplate,
"type": options && options.type,
"needs": [
"telemetry"
],
"delegation": true
}
],
"controllers": [
{
"key": "AutoflowTabularController",
"implementation": AutoflowTabularController,
"depends": [
"$scope",
"$timeout",
"telemetrySubscriber"
]
}
],
"directives": [
{
"key": "mctAutoflowTable",
"implementation": MCTAutoflowTable
}
]
}
});
openmct.legacyRegistry.enable("platform/features/autoflow");
};
};
});

View File

@@ -0,0 +1,26 @@
<div class="items-holder abs contents autoflow obj-value-format"
ng-controller="AutoflowTabularController as autoflow">
<div class="abs l-flex-row holder t-autoflow-header l-autoflow-header">
<mct-include key="'input-filter'"
ng-model="autoflow.filter"
class="flex-elem">
</mct-include>
<div class="flex-elem grows t-last-update" title="Last Update">{{autoflow.updated()}}</div>
<a title="Change column width"
class="s-button flex-elem icon-arrows-right-left change-column-width"
ng-click="autoflow.increaseColumnWidth()"></a>
</div>
<div class="abs t-autoflow-items l-autoflow-items"
mct-resize="autoflow.setBounds(bounds)"
mct-resize-interval="50">
<mct-autoflow-table values="autoflow.rangeValues()"
objects="autoflow.getTelemetryObjects()"
rows="autoflow.getRows()"
classes="autoflow.classes()"
updated="autoflow.updated()"
column-width="autoflow.columnWidth()"
counter="autoflow.counter()"
>
</mct-autoflow-table>
</div>
</div>

View File

@@ -0,0 +1,169 @@
/*global angular*/
define(
[],
function () {
/**
* The link step for the `mct-autoflow-table` directive;
* watches scope and updates the DOM appropriately.
* See documentation in `MCTAutoflowTable.js` for the rationale
* for including this directive, as well as for an explanation
* of which values are placed in scope.
*
* @constructor
* @param {Scope} scope the scope for this usage of the directive
* @param element the jqLite-wrapped element which used this directive
*/
function AutoflowTableLinker(scope, element) {
var objects, // Domain objects at last structure refresh
rows, // Number of rows from last structure refresh
priorClasses = {},
valueSpans = {}; // Span elements to put data values in
// Create a new name-value pair in the specified column
function createListItem(domainObject, ul) {
// Create a new li, and spans to go in it.
var li = angular.element('<li>'),
titleSpan = angular.element('<span>'),
valueSpan = angular.element('<span>');
// Place spans in the li, and li into the column.
// valueSpan must precede titleSpan in the DOM due to new CSS float approach
li.append(valueSpan).append(titleSpan);
ul.append(li);
// Style appropriately
li.addClass('l-autoflow-row');
titleSpan.addClass('l-autoflow-item l');
valueSpan.addClass('l-autoflow-item r l-obj-val-format');
// Set text/tooltip for the name-value row
titleSpan.text(domainObject.getModel().name);
titleSpan.attr("title", domainObject.getModel().name);
// Keep a reference to the span which will hold the
// data value, to populate in the next refreshValues call
valueSpans[domainObject.getId()] = valueSpan;
return li;
}
// Create a new column of name-value pairs in this table.
function createColumn(el) {
// Create a ul
var ul = angular.element('<ul>');
// Add it into the mct-autoflow-table
el.append(ul);
// Style appropriately
ul.addClass('l-autoflow-col');
// Get the current col width and apply at time of column creation
// Important to do this here, as new columns could be created after
// the user has changed the width.
ul.css('width', scope.columnWidth + 'px');
// Return it, so some li elements can be added
return ul;
}
// Change the width of the columns when user clicks the resize button.
function resizeColumn() {
element.find('ul').css('width', scope.columnWidth + 'px');
}
// Rebuild the DOM associated with this table.
function rebuild(domainObjects, rowCount) {
var activeColumn;
// Empty out our cached span elements
valueSpans = {};
// Start with an empty DOM beneath this directive
element.html("");
// Add DOM elements for each domain object being displayed
// in this table.
domainObjects.forEach(function (object, index) {
// Start a new column if we'd run out of room
if (index % rowCount === 0) {
activeColumn = createColumn(element);
}
// Add the DOM elements for that object to whichever
// column (a `ul` element) is current.
createListItem(object, activeColumn);
});
}
// Update spans with values, as made available via the
// `values` attribute of this directive.
function refreshValues() {
// Get the available values
var values = scope.values || {},
classes = scope.classes || {};
// Populate all spans with those values (or clear
// those spans if no value is available)
(objects || []).forEach(function (object) {
var id = object.getId(),
span = valueSpans[id],
value;
if (span) {
// Look up the value...
value = values[id];
// ...and convert to empty string if it's undefined
value = value === undefined ? "" : value;
span.attr("data-value", value);
// Update the span
span.text(value);
span.attr("title", value);
span.removeClass(priorClasses[id]);
span.addClass(classes[id]);
priorClasses[id] = classes[id];
}
// Also need stale/alert/ok class
// on span
});
}
// Refresh the DOM for this table, if necessary
function refreshStructure() {
// Only rebuild if number of rows or set of objects
// has changed; otherwise, our structure is still valid.
if (scope.objects !== objects ||
scope.rows !== rows) {
// Track those values to support future refresh checks
objects = scope.objects;
rows = scope.rows;
// Rebuild the DOM
rebuild(objects || [], rows || 1);
// Refresh all data values shown
refreshValues();
}
}
// Changing the domain objects in use or the number
// of rows should trigger a structure change (DOM rebuild)
scope.$watch("objects", refreshStructure);
scope.$watch("rows", refreshStructure);
// When the current column width has been changed, resize the column
scope.$watch('columnWidth', resizeColumn);
// When the last-updated time ticks,
scope.$watch("updated", refreshValues);
// Update displayed values when the counter changes.
scope.$watch("counter", refreshValues);
}
return AutoflowTableLinker;
}
);

View File

@@ -0,0 +1,324 @@
define(
['moment'],
function (moment) {
var ROW_HEIGHT = 16,
SLIDER_HEIGHT = 10,
INITIAL_COLUMN_WIDTH = 225,
MAX_COLUMN_WIDTH = 525,
COLUMN_WIDTH_STEP = 25,
DEBOUNCE_INTERVAL = 100,
DATE_FORMAT = "YYYY-DDD HH:mm:ss.SSS\\Z",
NOT_UPDATED = "No updates",
EMPTY_ARRAY = [];
/**
* Responsible for supporting the autoflow tabular view.
* Implements the all-over logic which drives that view,
* mediating between template-provided areas, the included
* `mct-autoflow-table` directive, and the underlying
* domain object model.
* @constructor
*/
function AutflowTabularController(
$scope,
$timeout,
telemetrySubscriber
) {
var filterValue = "",
filterValueLowercase = "",
subscription,
filteredObjects = [],
lastUpdated = {},
updateText = NOT_UPDATED,
rangeValues = {},
classes = {},
limits = {},
updatePending = false,
lastBounce = Number.NEGATIVE_INFINITY,
columnWidth = INITIAL_COLUMN_WIDTH,
rows = 1,
counter = 0;
// Trigger an update of the displayed table by incrementing
// the counter that it watches.
function triggerDisplayUpdate() {
counter += 1;
}
// Check whether or not an object's name matches the
// user-entered filter value.
function filterObject(domainObject) {
return (domainObject.getModel().name || "")
.toLowerCase()
.indexOf(filterValueLowercase) !== -1;
}
// Comparator for sorting points back into packet order
function compareObject(objectA, objectB) {
var indexA = objectA.getModel().index || 0,
indexB = objectB.getModel().index || 0;
return indexA - indexB;
}
// Update the list of currently-displayed objects; these
// will be the subset of currently subscribed-to objects
// which match a user-entered filter.
function doUpdateFilteredObjects() {
// Generate the list
filteredObjects = (
subscription ?
subscription.getTelemetryObjects() :
[]
).filter(filterObject).sort(compareObject);
// Clear the pending flag
updatePending = false;
// Track when this occurred, so that we can wait
// a whole before updating again.
lastBounce = Date.now();
triggerDisplayUpdate();
}
// Request an update to the list of current objects; this may
// run on a timeout to avoid excessive calls, e.g. while the user
// is typing a filter.
function updateFilteredObjects() {
// Don't do anything if an update is already scheduled
if (!updatePending) {
if (Date.now() > lastBounce + DEBOUNCE_INTERVAL) {
// Update immediately if it's been long enough
doUpdateFilteredObjects();
} else {
// Otherwise, update later, and track that we have
// an update pending so that subsequent calls can
// be ignored.
updatePending = true;
$timeout(doUpdateFilteredObjects, DEBOUNCE_INTERVAL);
}
}
}
// Track the latest data values for this domain object
function recordData(telemetryObject) {
// Get latest domain/range values for this object.
var id = telemetryObject.getId(),
domainValue = subscription.getDomainValue(telemetryObject),
rangeValue = subscription.getRangeValue(telemetryObject);
// Track the most recent timestamp change observed...
if (domainValue !== undefined && domainValue !== lastUpdated[id]) {
lastUpdated[id] = domainValue;
// ... and update the displayable text for that timestamp
updateText = isNaN(domainValue) ? "" :
moment.utc(domainValue).format(DATE_FORMAT);
}
// Store data values into the rangeValues structure, which
// will be used to populate the table itself.
// Note that we want full precision here.
rangeValues[id] = rangeValue;
// Update limit states as well
classes[id] = limits[id] && (limits[id].evaluate({
// This relies on external knowledge that the
// range value of a telemetry point is encoded
// in its datum as "value."
value: rangeValue
}) || {}).cssClass;
}
// Look at telemetry objects from the subscription; this is watched
// to detect changes from the subscription.
function subscribedTelemetry() {
return subscription ?
subscription.getTelemetryObjects() : EMPTY_ARRAY;
}
// Update the data values which will be used to populate the table
function updateValues() {
subscribedTelemetry().forEach(recordData);
triggerDisplayUpdate();
}
// Getter-setter function for user-entered filter text.
function filter(value) {
// If value was specified, we're a setter
if (value !== undefined) {
// Store the new value
filterValue = value;
filterValueLowercase = value.toLowerCase();
// Change which objects appear in the table
updateFilteredObjects();
}
// Always act as a getter
return filterValue;
}
// Update the bounds (width and height) of this view;
// called from the mct-resize directive. Recalculates how
// many rows should appear in the contained table.
function setBounds(bounds) {
var availableSpace = bounds.height - SLIDER_HEIGHT;
rows = Math.max(1, Math.floor(availableSpace / ROW_HEIGHT));
}
// Increment the current column width, up to the defined maximum.
// When the max is hit, roll back to the default.
function increaseColumnWidth() {
columnWidth += COLUMN_WIDTH_STEP;
// Cycle down to the initial width instead of exceeding max
columnWidth = columnWidth > MAX_COLUMN_WIDTH ?
INITIAL_COLUMN_WIDTH : columnWidth;
}
// Get displayable text for last-updated value
function updated() {
return updateText;
}
// Unsubscribe, if a subscription is active.
function releaseSubscription() {
if (subscription) {
subscription.unsubscribe();
subscription = undefined;
}
}
// Update set of telemetry objects managed by this view
function updateTelemetryObjects(telemetryObjects) {
updateFilteredObjects();
limits = {};
telemetryObjects.forEach(function (telemetryObject) {
var id = telemetryObject.getId();
limits[id] = telemetryObject.getCapability('limit');
});
}
// Create a subscription for the represented domain object.
// This will resolve capability delegation as necessary.
function makeSubscription(domainObject) {
// Unsubscribe, if there is an existing subscription
releaseSubscription();
// Clear updated timestamp
lastUpdated = {};
updateText = NOT_UPDATED;
// Create a new subscription; telemetrySubscriber gets
// to do the meaningful work here.
subscription = domainObject && telemetrySubscriber.subscribe(
domainObject,
updateValues
);
// Our set of in-view telemetry objects may have changed,
// so update the set that is being passed down to the table.
updateFilteredObjects();
}
// Watch for changes to the set of objects which have telemetry
$scope.$watch(subscribedTelemetry, updateTelemetryObjects);
// Watch for the represented domainObject (this field will
// be populated by mct-representation)
$scope.$watch("domainObject", makeSubscription);
// Make sure we unsubscribe when this view is destroyed.
$scope.$on("$destroy", releaseSubscription);
return {
/**
* Get the number of rows which should be shown in this table.
* @return {number} the number of rows to show
*/
getRows: function () {
return rows;
},
/**
* Get the objects which should currently be displayed in
* this table. This will be watched, so the return value
* should be stable when this list is unchanging. Only
* objects which match the user-entered filter value should
* be returned here.
* @return {DomainObject[]} the domain objects to include in
* this table.
*/
getTelemetryObjects: function () {
return filteredObjects;
},
/**
* Set the bounds (width/height) of this autoflow tabular view.
* The template must ensure that these bounds are tracked on
* the table area only.
* @param bounds the bounds; and object with `width` and
* `height` properties, both as numbers, in pixels.
*/
setBounds: setBounds,
/**
* Increments the width of the autoflow column.
* Setting does not yet persist.
*/
increaseColumnWidth: increaseColumnWidth,
/**
* Get-or-set the user-supplied filter value.
* @param {string} [value] the new filter value; omit to use
* as a getter
* @returns {string} the user-supplied filter value
*/
filter: filter,
/**
* Get all range values for use in this table. These will be
* returned as an object of key-value pairs, where keys are
* domain object IDs, and values are the most recently observed
* data values associated with those objects, formatted for
* display.
* @returns {object.<string,string>} most recent values
*/
rangeValues: function () {
return rangeValues;
},
/**
* Get CSS classes to apply to specific rows, representing limit
* states and/or stale states. These are returned as key-value
* pairs where keys are domain object IDs, and values are CSS
* classes to display for domain objects with those IDs.
* @returns {object.<string,string>} CSS classes
*/
classes: function () {
return classes;
},
/**
* Get the "last updated" text for this view; this will be
* the most recent timestamp observed for any telemetry-
* providing object, formatted for display.
* @returns {string} the time of the most recent update
*/
updated: updated,
/**
* Get the current column width, in pixels.
* @returns {number} column width
*/
columnWidth: function () {
return columnWidth;
},
/**
* Keep a counter and increment this whenever the display
* should be updated; this will be watched by the
* `mct-autoflow-table`.
* @returns {number} a counter value
*/
counter: function () {
return counter;
}
};
}
return AutflowTabularController;
}
);

View File

@@ -0,0 +1,60 @@
define(
["./AutoflowTableLinker"],
function (AutoflowTableLinker) {
/**
* The `mct-autoflow-table` directive specifically supports
* autoflow tabular views; it is not intended for use outside
* of that view.
*
* This directive is responsible for creating the structure
* of the table in this view, and for updating its values.
* While this is achievable using a regular Angular template,
* this is undesirable from the perspective of performance
* due to the number of watches that can be involved for large
* tables. Instead, this directive will maintain a small number
* of watches, rebuilding table structure only when necessary,
* and updating displayed values in the more common case of
* new data arriving.
*
* @constructor
*/
function MCTAutoflowTable() {
return {
// Only applicable at the element level
restrict: "E",
// The link function; handles DOM update/manipulation
link: AutoflowTableLinker,
// Parameters to pass from attributes into scope
scope: {
// Set of domain objects to show in the table
objects: "=",
// Values for those objects, by ID
values: "=",
// CSS classes to show for objects, by ID
classes: "=",
// Number of rows to show before autoflowing
rows: "=",
// Time of last update; watched to refresh values
updated: "=",
// Current width of the autoflow column
columnWidth: "=",
// A counter used to trigger display updates
counter: "="
}
};
}
return MCTAutoflowTable;
}
);

View File

@@ -0,0 +1,178 @@
define(
["../src/AutoflowTableLinker"],
function (AutoflowTableLinker) {
describe("The mct-autoflow-table linker", function () {
var cachedAngular,
mockAngular,
mockScope,
mockElement,
mockElements,
linker;
// Utility function to generate more mock elements
function createMockElement(html) {
var mockEl = jasmine.createSpyObj(
"element-" + html,
[
"append",
"addClass",
"removeClass",
"text",
"attr",
"html",
"css",
"find"
]
);
mockEl.testHtml = html;
mockEl.append.andReturn(mockEl);
mockElements.push(mockEl);
return mockEl;
}
function createMockDomainObject(id) {
var mockDomainObject = jasmine.createSpyObj(
"domainObject-" + id,
["getId", "getModel"]
);
mockDomainObject.getId.andReturn(id);
mockDomainObject.getModel.andReturn({name: id.toUpperCase()});
return mockDomainObject;
}
function fireWatch(watchExpression, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === watchExpression) {
call.args[1](value);
}
});
}
// AutoflowTableLinker accesses Angular in the global
// scope, since it is not injectable; we simulate that
// here by adding/removing it to/from the window object.
beforeEach(function () {
mockElements = [];
mockAngular = jasmine.createSpyObj("angular", ["element"]);
mockScope = jasmine.createSpyObj("scope", ["$watch"]);
mockElement = createMockElement('<div>');
mockAngular.element.andCallFake(createMockElement);
if (window.angular !== undefined) {
cachedAngular = window.angular;
}
window.angular = mockAngular;
linker = new AutoflowTableLinker(mockScope, mockElement);
});
afterEach(function () {
if (cachedAngular !== undefined) {
window.angular = cachedAngular;
} else {
delete window.angular;
}
});
it("watches for changes in inputs", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"objects",
jasmine.any(Function)
);
expect(mockScope.$watch).toHaveBeenCalledWith(
"rows",
jasmine.any(Function)
);
expect(mockScope.$watch).toHaveBeenCalledWith(
"counter",
jasmine.any(Function)
);
});
it("changes structure when domain objects change", function () {
// Set up scope
mockScope.rows = 4;
mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f']
.map(createMockDomainObject);
// Fire an update to the set of objects
fireWatch("objects");
// Should have rebuilt with two columns of
// four and two rows each; first, by clearing...
expect(mockElement.html).toHaveBeenCalledWith("");
// Should have appended two columns...
expect(mockElement.append.calls.length).toEqual(2);
// ...which should have received two and four rows each
expect(mockElement.append.calls[0].args[0].append.calls.length)
.toEqual(4);
expect(mockElement.append.calls[1].args[0].append.calls.length)
.toEqual(2);
});
it("updates values", function () {
var mockSpans;
mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f']
.map(createMockDomainObject);
mockScope.values = { a: 0 };
// Fire an update to the set of values
fireWatch("objects");
fireWatch("updated");
// Get all created spans
mockSpans = mockElements.filter(function (mockElem) {
return mockElem.testHtml === '<span>';
});
// First span should be a, should have gotten this value.
// This test detects, in particular, WTD-749
expect(mockSpans[0].text).toHaveBeenCalledWith('A');
expect(mockSpans[1].text).toHaveBeenCalledWith(0);
});
it("listens for changes in column width", function () {
var mockUL = createMockElement("<ul>");
mockElement.find.andReturn(mockUL);
mockScope.columnWidth = 200;
fireWatch("columnWidth", mockScope.columnWidth);
expect(mockUL.css).toHaveBeenCalledWith("width", "200px");
});
it("updates CSS classes", function () {
var mockSpans;
mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f']
.map(createMockDomainObject);
mockScope.values = { a: "a value to find" };
mockScope.classes = { a: 'class-a' };
// Fire an update to the set of values
fireWatch("objects");
fireWatch("updated");
// Figure out which span holds the relevant value...
mockSpans = mockElements.filter(function (mockElem) {
return mockElem.testHtml === '<span>';
}).filter(function (mockSpan) {
var attrCalls = mockSpan.attr.calls;
return attrCalls.some(function (call) {
return call.args[0] === 'title' &&
call.args[1] === mockScope.values.a;
});
});
// ...and make sure it also has had its class applied
expect(mockSpans[0].addClass)
.toHaveBeenCalledWith(mockScope.classes.a);
});
});
}
);

View File

@@ -0,0 +1,341 @@
define(
["../src/AutoflowTabularController"],
function (AutoflowTabularController) {
describe("The autoflow tabular controller", function () {
var mockScope,
mockTimeout,
mockSubscriber,
mockDomainObject,
mockSubscription,
controller;
// Fire watches that are registered as functions.
function fireFnWatches() {
mockScope.$watch.calls.forEach(function (call) {
if (typeof call.args[0] === 'function') {
call.args[1](call.args[0]());
}
});
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$on", "$watch"]
);
mockTimeout = jasmine.createSpy("$timeout");
mockSubscriber = jasmine.createSpyObj(
"telemetrySubscriber",
["subscribe"]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
);
mockSubscription = jasmine.createSpyObj(
"subscription",
[
"unsubscribe",
"getTelemetryObjects",
"getDomainValue",
"getRangeValue"
]
);
mockSubscriber.subscribe.andReturn(mockSubscription);
mockDomainObject.getModel.andReturn({name: "something"});
controller = new AutoflowTabularController(
mockScope,
mockTimeout,
mockSubscriber
);
});
it("listens for the represented domain object", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"domainObject",
jasmine.any(Function)
);
});
it("provides a getter-setter function for filtering", function () {
expect(controller.filter()).toEqual("");
controller.filter("something");
expect(controller.filter()).toEqual("something");
});
it("tracks bounds and adjust number of rows accordingly", function () {
// Rows are 15px high, and need room for an 10px slider
controller.setBounds({ width: 700, height: 120 });
expect(controller.getRows()).toEqual(6); // 110 usable height / 16px
controller.setBounds({ width: 700, height: 240 });
expect(controller.getRows()).toEqual(14); // 230 usable height / 16px
});
it("subscribes to a represented object's telemetry", function () {
// Set up subscription, scope
mockSubscription.getTelemetryObjects
.andReturn([mockDomainObject]);
mockScope.domainObject = mockDomainObject;
// Invoke the watcher with represented domain object
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Should have subscribed to it
expect(mockSubscriber.subscribe).toHaveBeenCalledWith(
mockDomainObject,
jasmine.any(Function)
);
// Should report objects as reported from subscription
expect(controller.getTelemetryObjects())
.toEqual([mockDomainObject]);
});
it("releases subscriptions on destroy", function () {
// Set up subscription...
mockSubscription.getTelemetryObjects
.andReturn([mockDomainObject]);
mockScope.domainObject = mockDomainObject;
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Verify precondition
expect(mockSubscription.unsubscribe).not.toHaveBeenCalled();
// Make sure we're listening for $destroy
expect(mockScope.$on).toHaveBeenCalledWith(
"$destroy",
jasmine.any(Function)
);
// Fire a destroy event
mockScope.$on.mostRecentCall.args[1]();
// Should have unsubscribed
expect(mockSubscription.unsubscribe).toHaveBeenCalled();
});
it("presents latest values and latest update state", function () {
// Make sure values are available
mockSubscription.getDomainValue.andReturn(402654321123);
mockSubscription.getRangeValue.andReturn(789);
mockDomainObject.getId.andReturn('testId');
// Set up subscription...
mockSubscription.getTelemetryObjects
.andReturn([mockDomainObject]);
mockScope.domainObject = mockDomainObject;
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Fire subscription callback
mockSubscriber.subscribe.mostRecentCall.args[1]();
// ...and exposed the results for template to consume
expect(controller.updated()).toEqual("1982-278 08:25:21.123Z");
expect(controller.rangeValues().testId).toEqual(789);
});
it("sorts domain objects by index", function () {
var testIndexes = { a: 2, b: 1, c: 3, d: 0 },
mockDomainObjects = Object.keys(testIndexes).sort().map(function (id) {
var mockDomainObj = jasmine.createSpyObj(
"domainObject",
["getId", "getModel"]
);
mockDomainObj.getId.andReturn(id);
mockDomainObj.getModel.andReturn({ index: testIndexes[id] });
return mockDomainObj;
});
// Expose those domain objects...
mockSubscription.getTelemetryObjects.andReturn(mockDomainObjects);
mockScope.domainObject = mockDomainObject;
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
// Fire subscription callback
mockSubscriber.subscribe.mostRecentCall.args[1]();
// Controller should expose same objects, but sorted by index from model
expect(controller.getTelemetryObjects()).toEqual([
mockDomainObjects[3], // d, index=0
mockDomainObjects[1], // b, index=1
mockDomainObjects[0], // a, index=2
mockDomainObjects[2] // c, index=3
]);
});
it("uses a timeout to throttle update", function () {
// Set up subscription...
mockSubscription.getTelemetryObjects
.andReturn([mockDomainObject]);
mockScope.domainObject = mockDomainObject;
// Set the object in view; should not need a timeout
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockTimeout.calls.length).toEqual(0);
// Next call should schedule an update on a timeout
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockTimeout.calls.length).toEqual(1);
// ...but this last one should not, since existing
// timeout will cover it
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockTimeout.calls.length).toEqual(1);
});
it("allows changing column width", function () {
var initialWidth = controller.columnWidth();
controller.increaseColumnWidth();
expect(controller.columnWidth()).toBeGreaterThan(initialWidth);
});
describe("filter", function () {
var doFilter,
filteredObjects,
filteredObjectNames;
beforeEach(function () {
var telemetryObjects,
updateFilteredObjects;
telemetryObjects = [
'DEF123',
'abc789',
'456abc',
'4ab3cdef',
'hjs[12].*(){}^\\'
].map(function (objectName, index) {
var mockTelemetryObject = jasmine.createSpyObj(
objectName,
["getId", "getModel"]
);
mockTelemetryObject.getId.andReturn(objectName);
mockTelemetryObject.getModel.andReturn({
name: objectName,
index: index
});
return mockTelemetryObject;
});
mockSubscription
.getTelemetryObjects
.andReturn(telemetryObjects);
// Trigger domainObject change to create subscription.
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
updateFilteredObjects = function () {
filteredObjects = controller.getTelemetryObjects();
filteredObjectNames = filteredObjects.map(function (o) {
return o.getModel().name;
});
};
doFilter = function (term) {
controller.filter(term);
// Filter is debounced so we have to force it to occur.
mockTimeout.mostRecentCall.args[0]();
updateFilteredObjects();
};
updateFilteredObjects();
});
it("initially shows all objects", function () {
expect(filteredObjectNames).toEqual([
'DEF123',
'abc789',
'456abc',
'4ab3cdef',
'hjs[12].*(){}^\\'
]);
});
it("by blank string matches all objects", function () {
doFilter('');
expect(filteredObjectNames).toEqual([
'DEF123',
'abc789',
'456abc',
'4ab3cdef',
'hjs[12].*(){}^\\'
]);
});
it("exactly matches an object name", function () {
doFilter('4ab3cdef');
expect(filteredObjectNames).toEqual(['4ab3cdef']);
});
it("partially matches object names", function () {
doFilter('abc');
expect(filteredObjectNames).toEqual([
'abc789',
'456abc'
]);
});
it("matches case insensitive names", function () {
doFilter('def');
expect(filteredObjectNames).toEqual([
'DEF123',
'4ab3cdef'
]);
});
it("works as expected with special characters", function () {
doFilter('[12]');
expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']);
doFilter('.*');
expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']);
doFilter('.*()');
expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']);
doFilter('.*?');
expect(filteredObjectNames).toEqual([]);
doFilter('.+');
expect(filteredObjectNames).toEqual([]);
});
it("exposes CSS classes from limits", function () {
var id = mockDomainObject.getId(),
testClass = "some-css-class",
mockLimitCapability =
jasmine.createSpyObj('limit', ['evaluate']);
mockDomainObject.getCapability.andCallFake(function (key) {
return key === 'limit' && mockLimitCapability;
});
mockLimitCapability.evaluate
.andReturn({ cssClass: testClass });
mockSubscription.getTelemetryObjects
.andReturn([mockDomainObject]);
fireFnWatches();
mockSubscriber.subscribe.mostRecentCall.args[1]();
expect(controller.classes()[id]).toEqual(testClass);
});
it("exposes a counter that changes with each update", function () {
var i, prior;
for (i = 0; i < 10; i += 1) {
prior = controller.counter();
expect(controller.counter()).toEqual(prior);
mockSubscriber.subscribe.mostRecentCall.args[1]();
expect(controller.counter()).not.toEqual(prior);
}
});
});
});
}
);

View File

@@ -0,0 +1,39 @@
define(
["../src/MCTAutoflowTable"],
function (MCTAutoflowTable) {
describe("The mct-autoflow-table directive", function () {
var mctAutoflowTable;
beforeEach(function () {
mctAutoflowTable = new MCTAutoflowTable();
});
// Real functionality is contained/tested in the linker,
// so just check to make sure we're exposing the directive
// appropriately.
it("is applicable at the element level", function () {
expect(mctAutoflowTable.restrict).toEqual("E");
});
it("two-ways binds needed scope variables", function () {
expect(mctAutoflowTable.scope).toEqual({
objects: "=",
values: "=",
rows: "=",
updated: "=",
classes: "=",
columnWidth: "=",
counter: "="
});
});
it("provides a link function", function () {
expect(mctAutoflowTable.link).toEqual(jasmine.any(Function));
});
});
}
);

View File

@@ -36,9 +36,7 @@ define(
* @param {ActionContext} context the context for this action
*/
function FollowTimerAction(timerService, context) {
var domainObject =
context.domainObject &&
context.domainObject.useCapability('adapter');
var domainObject = context.domainObject;
this.perform =
timerService.setTimer.bind(timerService, domainObject);
}

View File

@@ -45,7 +45,7 @@ define(
FollowIndicator.prototype.getText = function () {
var timer = this.timerService.getTimer();
return timer ? ('Following timer ' + timer.name) : NO_TIMER;
return (timer) ? 'Following timer ' + timer.getModel().name : NO_TIMER;
};
FollowIndicator.prototype.getDescription = function () {

View File

@@ -32,7 +32,6 @@ define(['EventEmitter'], function (EventEmitter) {
function TimerService(openmct) {
EventEmitter.apply(this);
this.time = openmct.time;
this.objects = openmct.objects;
}
TimerService.prototype = Object.create(EventEmitter.prototype);
@@ -45,16 +44,6 @@ define(['EventEmitter'], function (EventEmitter) {
TimerService.prototype.setTimer = function (timer) {
this.timer = timer;
this.emit('change');
if (this.stopObserving) {
this.stopObserving();
delete this.stopObserving;
}
if (timer) {
this.stopObserving =
this.objects.observe(timer, '*', this.setTimer.bind(this));
}
};
/**
@@ -84,16 +73,16 @@ define(['EventEmitter'], function (EventEmitter) {
var clock = this.time.clock();
var canConvert = this.hasTimer() &&
!!clock &&
this.timer.timerState !== 'stopped';
this.timer.getModel().timerState !== 'stopped';
if (!canConvert) {
return undefined;
}
var now = clock.currentValue();
var delta = this.timer.timerState === 'paused' ?
now - this.timer.pausedTime : 0;
var epoch = this.timer.timestamp;
var model = this.timer.getModel();
var delta = model.timerState === 'paused' ? now - model.pausedTime : 0;
var epoch = model.timestamp;
return timestamp - epoch - delta;
};

View File

@@ -29,19 +29,12 @@ define([
describe("The Follow Timer action", function () {
var testContext;
var testModel;
var testAdaptedObject;
beforeEach(function () {
testModel = {};
testContext = { domainObject: jasmine.createSpyObj('domainObject', [
'getModel',
'useCapability'
]) };
testAdaptedObject = { foo: 'bar' };
testContext.domainObject.getModel.andReturn(testModel);
testContext.domainObject.useCapability.andCallFake(function (c) {
return c === 'adapter' && testAdaptedObject;
});
testContext = { domainObject: { getModel: function () {
return testModel;
} } };
});
it("is applicable to timers", function () {
@@ -79,7 +72,7 @@ define([
it("sets the active timer", function () {
expect(mockTimerService.setTimer)
.toHaveBeenCalledWith(testAdaptedObject);
.toHaveBeenCalledWith(testContext.domainObject);
});
});
});

View File

@@ -42,15 +42,18 @@ define(["../../src/indicators/FollowIndicator"], function (FollowIndicator) {
});
describe("when a timer is set", function () {
var testObject;
var testModel;
var mockDomainObject;
beforeEach(function () {
testObject = { name: "some timer!" };
mockTimerService.getTimer.andReturn(testObject);
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(testObject.name))
expect(indicator.getText().indexOf(testModel.name))
.not.toEqual(-1);
});
});

View File

@@ -30,10 +30,7 @@ define([
beforeEach(function () {
callback = jasmine.createSpy('callback');
mockmct = {
time: { clock: jasmine.createSpy('clock') },
objects: { observe: jasmine.createSpy('observe') }
};
mockmct = { time: { clock: jasmine.createSpy('clock') } };
timerService = new TimerService(mockmct);
timerService.on('change', callback);
});
@@ -61,17 +58,6 @@ define([
it("reports the current timer", function () {
expect(timerService.getTimer()).toBe(testTimer);
});
it("observes changes to an object", function () {
var newTimer = { name: "I am another timer." };
expect(mockmct.objects.observe).toHaveBeenCalledWith(
testTimer,
'*',
jasmine.any(Function)
);
mockmct.objects.observe.mostRecentCall.args[2](newTimer);
expect(timerService.getTimer()).toBe(newTimer);
});
});
});
});

View File

@@ -255,8 +255,6 @@ define(
if (this.nextDatum) {
this.updateValues(this.nextDatum);
delete this.nextDatum;
} else {
this.updateValues(this.$scope.imageHistory[this.$scope.imageHistory.length - 1]);
}
this.autoScroll = true;
}

View File

@@ -183,17 +183,6 @@ define(
expect(controller.getImageUrl()).toEqual(newUrl);
});
it("forwards large image view to latest image in history on un-pause", function () {
$scope.imageHistory = [
{ utc: 1434600258122, url: 'some/url1', selected: false},
{ utc: 1434600258123, url: 'some/url2', selected: false}
];
controller.paused(true);
controller.paused(false);
expect(controller.getImageUrl()).toEqual(controller.getImageUrl($scope.imageHistory[1]));
});
it("subscribes to telemetry", function () {
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
newDomainObject,
@@ -238,7 +227,7 @@ define(
expect(controller.updateHistory(mockDatum)).toBe(false);
});
describe("when user clicks on imagery thumbnail", function () {
describe("user clicks on imagery thumbnail", function () {
var mockDatum = { utc: 1434600258123, url: 'some/url', selected: false};
it("pauses and adds selected class to imagery thumbnail", function () {
@@ -259,7 +248,6 @@ define(
expect(controller.getTime()).toEqual(controller.timeFormat.format(mockDatum.utc));
});
});
});
it("initially shows an empty string for date/time", function () {

View File

@@ -260,9 +260,7 @@ define([
"key": "LayoutController",
"implementation": LayoutController,
"depends": [
"$scope",
"$element",
"openmct"
"$scope"
]
},
{
@@ -272,8 +270,7 @@ define([
"$scope",
"$q",
"dialogService",
"openmct",
"$element"
"openmct"
]
}
],

View File

@@ -23,7 +23,7 @@
ng-controller="FixedController as controller">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.bypassSelection($event)">
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
@@ -35,28 +35,33 @@
<!-- Fixed position elements -->
<div ng-repeat="element in controller.getElements()"
class="l-fixed-position-item s-selectable s-moveable s-hover-border"
ng-class="{
's-not-selected': controller.selected() && !controller.selected(element),
's-selected': controller.selected(element)
}"
ng-style="element.style"
mct-selectable="controller.getContext(element)"
mct-init-select="controller.shouldSelect(element)">
ng-click="controller.select(element)">
<mct-include key="element.template"
parameters="{ gridSize: controller.getGridSize() }"
ng-model="element">
</mct-include>
</mct-include>
</div>
<!-- Selection highlight, handles -->
<span class="s-selected s-moveable" ng-if="controller.isElementSelected()">
<span class="s-selected s-moveable" ng-if="controller.selected()">
<div class="l-fixed-position-item t-edit-handle-holder"
mct-drag-down="controller.moveHandle().startDrag()"
mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
mct-drag="controller.moveHandle().continueDrag(delta)"
mct-drag-up="controller.endDrag()"
ng-style="controller.getSelectedElementStyle()">
mct-drag-up="controller.moveHandle().endDrag()"
ng-style="controller.selected().style">
</div>
<div ng-repeat="handle in controller.handles()"
class="l-fixed-position-item-handle edit-corner"
ng-style="handle.style()"
mct-drag-down="handle.startDrag()"
mct-drag="handle.continueDrag(delta)"
mct-drag-up="controller.endDrag(handle)">
mct-drag-up="handle.endDrag()">
</div>
</span>
</div>

View File

@@ -22,12 +22,10 @@
<div class="abs l-layout"
ng-controller="LayoutController as controller"
ng-click="controller.bypassSelection($event)">
ng-click="controller.clearSelection()">
<!-- Background grid -->
<div class="l-grid-holder"
ng-show="!controller.drilledIn"
ng-click="controller.bypassSelection($event)">
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
@@ -36,13 +34,10 @@
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<div class="abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border t-object-type-{{ childObject.getModel().type }}"
data-layout-id="{{childObject.getId() + '-' + $id}}"
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-drilled-in': controller.isDrilledIn(childObject) }"
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
ng-repeat="childObject in composition"
ng-init="controller.selectIfNew(childObject.getId() + '-' + $id, childObject)"
mct-selectable="controller.getContext(childObject, true)"
ng-dblclick="controller.drill($event, childObject)"
ng-click="controller.select($event, childObject.getId())"
ng-style="controller.getFrameStyle(childObject.getId())">
<mct-representation key="'frame'"
@@ -50,7 +45,7 @@
mct-object="childObject">
</mct-representation>
<!-- Drag handles -->
<span class="abs t-edit-handle-holder" ng-if="controller.selected(childObject) && !controller.isDrilledIn(childObject)">
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
<span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)"
@@ -78,6 +73,7 @@
mct-drag-up="controller.endDrag()">
</span>
</span>
</div>
</div>

View File

@@ -47,7 +47,7 @@ define(
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function FixedController($scope, $q, dialogService, openmct, $element) {
function FixedController($scope, $q, dialogService, openmct) {
this.names = {}; // Cache names by ID
this.values = {}; // Cache values by ID
this.elementProxiesById = {};
@@ -55,11 +55,9 @@ define(
this.telemetryObjects = [];
this.subscriptions = [];
this.openmct = openmct;
this.$element = $element;
this.$scope = $scope;
this.gridSize = $scope.domainObject && $scope.domainObject.getModel().layoutGrid;
this.fixedViewSelectable = false;
var self = this;
[
@@ -89,8 +87,9 @@ define(
// Update the style for a selected element
function updateSelectionStyle() {
if (self.selectedElementProxy) {
self.selectedElementProxy.style = convertPosition(self.selectedElementProxy);
var element = self.selection && self.selection.get();
if (element) {
element.style = convertPosition(element);
}
}
@@ -137,19 +136,25 @@ define(
// Decorate elements in the current configuration
function refreshElements() {
var elements = (($scope.configuration || {}).elements || []);
// Cache selection; we are instantiating new proxies
// so we may want to restore this.
var selected = self.selection && self.selection.get(),
elements = (($scope.configuration || {}).elements || []),
index = -1; // Start with a 'not-found' value
// Find the selection in the new array
if (selected !== undefined) {
index = elements.indexOf(selected.element);
}
// Create the new proxies...
self.elementProxies = elements.map(makeProxyElement);
// If selection is not in array, select parent.
// Otherwise, set the element to select after refresh.
if (self.selectedElementProxy) {
var index = elements.indexOf(self.selectedElementProxy.element);
if (index === -1) {
self.$element[0].click();
} else if (!self.elementToSelectAfterRefresh) {
self.elementToSelectAfterRefresh = self.elementProxies[index].element;
// Clear old selection, and restore if appropriate
if (self.selection) {
self.selection.deselect();
if (index > -1) {
self.select(self.elementProxies[index]);
}
}
@@ -219,12 +224,12 @@ define(
$scope.configuration.elements || [];
// Store the position of this element.
$scope.configuration.elements.push(element);
self.elementToSelectAfterRefresh = element;
// Refresh displayed elements
refreshElements();
// Select the newly-added element
self.select(
self.elementProxies[self.elementProxies.length - 1]
);
// Mark change as persistable
if ($scope.commit) {
$scope.commit("Dropped an element.");
@@ -258,36 +263,21 @@ define(
self.getTelemetry($scope.domainObject);
}
// Sets the selectable object in response to the selection change event.
function setSelection(selectable) {
var selection = selectable[0];
if (!selection) {
return;
}
if (selection.context.elementProxy) {
self.selectedElementProxy = selection.context.elementProxy;
self.mvHandle = self.generateDragHandle(self.selectedElementProxy);
self.resizeHandles = self.generateDragHandles(self.selectedElementProxy);
} else {
// Make fixed view selectable if it's not already.
if (!self.fixedViewSelectable) {
self.fixedViewSelectable = true;
selection.context.viewProxy = new FixedProxy(addElement, $q, dialogService);
self.openmct.selection.select(selection);
}
self.resizeHandles = [];
self.mvHandle = undefined;
self.selectedElementProxy = undefined;
}
}
this.elementProxies = [];
this.generateDragHandle = generateDragHandle;
this.generateDragHandles = generateDragHandles;
this.updateSelectionStyle = updateSelectionStyle;
// Track current selection state
$scope.$watch("selection", function (selection) {
this.selection = selection;
// Expose the view's selection proxy
if (this.selection) {
this.selection.proxy(
new FixedProxy(addElement, $q, dialogService)
);
}
}.bind(this));
// Detect changes to grid size
$scope.$watch("model.layoutGrid", updateElementPositions);
@@ -308,13 +298,10 @@ define(
$scope.$on("$destroy", function () {
self.unsubscribe();
self.openmct.time.off("bounds", updateDisplayBounds);
self.openmct.selection.off("change", setSelection);
});
// Respond to external bounds changes
this.openmct.time.on("bounds", updateDisplayBounds);
this.openmct.selection.on('change', setSelection);
this.$element.on('click', this.bypassSelection.bind(this));
}
/**
@@ -373,47 +360,22 @@ define(
*/
FixedController.prototype.updateView = function (telemetryObject, datum) {
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
var rangeMetadata = metadata.valuesForHints(['range'])[0];
var rangeKey = rangeMetadata.source || rangeMetadata.key;
var valueMetadata = metadata.value(rangeKey);
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
var value = datum[valueMetadata.key];
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, rangeKey);
this.setDisplayedValue(
telemetryObject,
formattedTelemetryValue,
formatter.format(value),
alarm && alarm.cssClass
);
this.digest();
};
/**
* @private
*/
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
var valueMetadata = metadata.value(telemetryKeyToDisplay);
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
return formatter.format(datum[valueMetadata.key]);
};
/**
* @private
*/
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
// If there is a range value, show that preferentially
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
// If no range is defined, default to the highest priority non time-domain data.
if (telemetryKeyToDisplay === undefined) {
var valuesOrderedByPriority = metadata.values();
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
return !(valueMetadata.hints.domain);
})[0];
}
return telemetryKeyToDisplay.source;
};
/**
* Request the last historical data point for the given domain objects
* @param {object[]} objects
@@ -426,9 +388,7 @@ define(
objects.forEach(function (object) {
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
.then(function (data) {
if (data.length > 0) {
self.updateView(object, data[data.length - 1]);
}
self.updateView(object, data[data.length - 1]);
});
});
return objects;
@@ -505,56 +465,38 @@ define(
};
/**
* Checks if the element should be selected or not.
*
* @param elementProxy the element to check
* @returns {boolean} true if the element should be selected.
* Check if the element is currently selected, or (if no
* argument is supplied) get the currently selected element.
* @returns {boolean} true if selected
*/
FixedController.prototype.shouldSelect = function (elementProxy) {
if (elementProxy.element === this.elementToSelectAfterRefresh) {
delete this.elementToSelectAfterRefresh;
return true;
} else {
return false;
FixedController.prototype.selected = function (element) {
var selection = this.selection;
return selection && ((arguments.length > 0) ?
selection.selected(element) : selection.get());
};
/**
* Set the active user selection in this view.
* @param element the element to select
*/
FixedController.prototype.select = function select(element) {
if (this.selection) {
// Update selection...
this.selection.select(element);
// ...as well as move, resize handles
this.mvHandle = this.generateDragHandle(element);
this.resizeHandles = this.generateDragHandles(element);
}
};
/**
* Checks if an element is currently selected.
*
* @returns {boolean} true if an element is selected.
* Clear the current user selection.
*/
FixedController.prototype.isElementSelected = function () {
return (this.selectedElementProxy) ? true : false;
};
/**
* Gets the style for the selected element.
*
* @returns {string} element style
*/
FixedController.prototype.getSelectedElementStyle = function () {
return (this.selectedElementProxy) ? this.selectedElementProxy.style : undefined;
};
/**
* Gets the selected element.
*
* @returns the selected element
*/
FixedController.prototype.getSelectedElement = function () {
return this.selectedElementProxy;
};
/**
* Prevents the event from bubbling up if drag is in progress.
*/
FixedController.prototype.bypassSelection = function ($event) {
if (this.dragInProgress) {
if ($event) {
$event.stopPropagation();
}
return;
FixedController.prototype.clearSelection = function () {
if (this.selection) {
this.selection.deselect();
this.resizeHandles = [];
this.mvHandle = undefined;
}
};
@@ -575,38 +517,6 @@ define(
return this.mvHandle;
};
/**
* Gets the selection context.
*
* @param elementProxy the element proxy
* @returns {object} the context object which includes elementProxy and toolbar
*/
FixedController.prototype.getContext = function (elementProxy) {
return {
elementProxy: elementProxy,
toolbar: elementProxy
};
};
/**
* End drag.
*
* @param handle the resize handle
*/
FixedController.prototype.endDrag = function (handle) {
this.dragInProgress = true;
setTimeout(function () {
this.dragInProgress = false;
}.bind(this), 0);
if (handle) {
handle.endDrag();
} else {
this.moveHandle().endDrag();
}
};
return FixedController;
}
);

View File

@@ -65,7 +65,7 @@ define(
* Start a drag gesture. This should be called when a drag
* begins to track initial state.
*/
FixedDragHandle.prototype.startDrag = function () {
FixedDragHandle.prototype.startDrag = function startDrag() {
// Cache initial x/y positions
this.dragging = {
x: this.elementHandle.x(),

View File

@@ -26,14 +26,8 @@
* @namespace platform/features/layout
*/
define(
[
'zepto',
'./LayoutDrag'
],
function (
$,
LayoutDrag
) {
['./LayoutDrag'],
function (LayoutDrag) {
var DEFAULT_DIMENSIONS = [12, 8],
DEFAULT_GRID_SIZE = [32, 32],
@@ -52,12 +46,10 @@ define(
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function LayoutController($scope, $element, openmct) {
function LayoutController($scope) {
var self = this,
callbackCount = 0;
this.$element = $element;
// Update grid size when it changed
function updateGridSize(layoutGrid) {
var oldSize = self.gridSize;
@@ -127,11 +119,10 @@ define(
self.layoutPanels(ids);
self.setFrames(ids);
if (self.selectedId &&
self.selectedId !== $scope.domainObject.getId() &&
composition.indexOf(self.selectedId) === -1) {
// Click triggers selection of layout parent.
self.$element[0].click();
// If there is a newly-dropped object, select it.
if (self.droppedIdToSelectAfterRefresh) {
self.select(null, self.droppedIdToSelectAfterRefresh);
delete self.droppedIdToSelectAfterRefresh;
}
}
});
@@ -163,39 +154,22 @@ define(
}
};
// Sets the selectable object in response to the selection change event.
function setSelection(selectable) {
var selection = selectable[0];
if (!selection) {
delete self.selectedId;
return;
}
self.selectedId = selection.context.oldItem.getId();
self.drilledIn = undefined;
self.selectable = selectable;
}
this.positions = {};
this.rawPositions = {};
this.gridSize = DEFAULT_GRID_SIZE;
this.$scope = $scope;
this.drilledIn = undefined;
this.openmct = openmct;
// Watch for changes to the grid size in the model
$scope.$watch("model.layoutGrid", updateGridSize);
$scope.$watch("selection", function (selection) {
this.selection = selection;
}.bind(this));
// Update composed objects on screen, and position panes
$scope.$watchCollection("model.composition", refreshComposition);
openmct.selection.on('change', setSelection);
$scope.$on("$destroy", function () {
openmct.selection.off("change", setSelection);
});
// Position panes where they are dropped
$scope.$on("mctDrop", handleDrop);
}
@@ -377,14 +351,37 @@ define(
};
/**
* Checks if the object is currently selected.
* Check if the object is currently selected.
*
* @param {string} obj the object to check for selection
* @returns {boolean} true if selected, otherwise false
*/
LayoutController.prototype.selected = function (obj) {
var sobj = this.openmct.selection.get()[0];
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
return !!this.selectedId && this.selectedId === obj.getId();
};
/**
* Set the active user selection in this view.
*
* @param event the mouse event
* @param {string} id the object id
*/
LayoutController.prototype.select = function (event, id) {
if (event) {
event.stopPropagation();
if (this.selection) {
event.preventDefault();
}
}
this.selectedId = id;
var selectedObj = {};
selectedObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id);
if (this.selection) {
this.selection.select(selectedObj);
}
};
/**
@@ -393,7 +390,7 @@ define(
* @param {string} id the object id
* @private
*/
LayoutController.prototype.toggleFrame = function (id, domainObject) {
LayoutController.prototype.toggleFrame = function (id) {
var configuration = this.$scope.configuration;
if (!configuration.panels[id]) {
@@ -401,75 +398,21 @@ define(
}
this.frames[id] = configuration.panels[id].hasFrame = !this.frames[id];
var selection = this.openmct.selection.get();
selection[0].context.toolbar = this.getToolbar(id, domainObject);
this.openmct.selection.select(selection); // reselect so toolbar updates
this.select(undefined, id); // reselect so toolbar updates
};
/**
* Gets the toolbar object for the given domain object.
*
* @param id the domain object id
* @param domainObject the domain object
* @returns {object}
* @private
* Clear the current user selection.
*/
LayoutController.prototype.getToolbar = function (id, domainObject) {
var toolbarObj = {};
toolbarObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id, domainObject);
return toolbarObj;
};
/**
* Bypasses selection if drag is in progress.
*
* @param event the angular event object
*/
LayoutController.prototype.bypassSelection = function (event) {
LayoutController.prototype.clearSelection = function () {
if (this.dragInProgress) {
if (event) {
event.stopPropagation();
}
return;
}
};
/**
* Checks if the domain object is drilled in.
*
* @param domainObject the domain object
* @return true if the object is drilled in, false otherwise
*/
LayoutController.prototype.isDrilledIn = function (domainObject) {
return this.drilledIn === domainObject.getId();
};
/**
* Puts the given object in the drilled-in mode.
*
* @param event the angular event object
* @param domainObject the domain object
*/
LayoutController.prototype.drill = function (event, domainObject) {
if (event) {
event.stopPropagation();
}
if (!domainObject.getCapability('editor').inEditContext()) {
return;
}
if (!domainObject.hasCapability('composition')) {
return;
if (this.selection) {
this.selection.deselect();
delete this.selectedId;
}
// Disable since fixed position doesn't use the selection API yet
if (domainObject.getModel().type === 'telemetry.fixed') {
return;
}
this.drilledIn = domainObject.getId();
};
/**
@@ -491,36 +434,6 @@ define(
return this.gridSize;
};
/**
* Gets the selection context.
*
* @param domainObject the domain object
* @returns {object} the context object which includes
* item, oldItem and toolbar
*/
LayoutController.prototype.getContext = function (domainObject, toolbar) {
return {
item: domainObject.useCapability('adapter'),
oldItem: domainObject,
toolbar: toolbar ? this.getToolbar(domainObject.getId(), domainObject) : undefined
};
};
/**
* Selects a newly-dropped object.
*
* @param classSelector the css class selector
* @param domainObject the domain object
*/
LayoutController.prototype.selectIfNew = function (selector, domainObject) {
if (domainObject.getId() === this.droppedIdToSelectAfterRefresh) {
setTimeout(function () {
$('[data-layout-id="' + selector + '"]')[0].click();
delete this.droppedIdToSelectAfterRefresh;
}.bind(this), 0);
}
};
return LayoutController;
}
);

View File

@@ -77,7 +77,6 @@ define([
overlayContainer;
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');

View File

@@ -55,8 +55,8 @@ define(
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {Array} elements the full array of elements
* @param {number[]} gridSize the current layout grid size in [x,y] from
* @param {Array} elements the full array of elements
*/
function ElementProxy(element, index, elements, gridSize) {
/**

View File

@@ -21,14 +21,8 @@
*****************************************************************************/
define(
[
"../src/FixedController",
"zepto"
],
function (
FixedController,
$
) {
["../src/FixedController"],
function (FixedController) {
describe("The Fixed Position controller", function () {
var mockScope,
@@ -52,9 +46,6 @@ define(
mockMetadata,
mockTimeSystem,
mockLimitEvaluator,
mockSelection,
$element = [],
selectable = [],
controller;
// Utility function; find a watch for a given expression
@@ -187,36 +178,23 @@ define(
Promise.resolve(mockChildren)
);
mockScope.model = testModel;
mockScope.configuration = testConfiguration;
selectable[0] = {
context: {
oldItem: mockDomainObject
}
};
mockSelection = jasmine.createSpyObj("selection", [
'select',
'on',
'off',
'get'
]);
mockSelection.get.andCallThrough();
mockScope.selection = jasmine.createSpyObj(
'selection',
['select', 'get', 'selected', 'deselect', 'proxy']
);
mockOpenMCT = {
time: mockConductor,
telemetry: mockTelemetryAPI,
composition: mockCompositionAPI,
selection: mockSelection
composition: mockCompositionAPI
};
$element = $('<div></div>');
spyOn($element[0], 'click');
mockMetadata = jasmine.createSpyObj('mockMetadata', [
'valuesForHints',
'value',
'values'
'value'
]);
mockMetadata.value.andReturn({
key: 'value'
@@ -248,11 +226,11 @@ define(
mockScope,
mockQ,
mockDialogService,
mockOpenMCT,
$element
mockOpenMCT
);
findWatch("model.layoutGrid")(testModel.layoutGrid);
findWatch("selection")(mockScope.selection);
});
it("subscribes when a domain object is available", function () {
@@ -328,41 +306,41 @@ define(
});
it("allows elements to be selected", function () {
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
expect(controller.isElementSelected()).toBe(true);
});
it("allows selection retrieval", function () {
var elements;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
expect(controller.getSelectedElement()).toEqual(elements[1]);
controller.select(elements[1]);
expect(mockScope.selection.select)
.toHaveBeenCalledWith(elements[1]);
});
it("selects the parent view when selected element is removed", function () {
it("allows selection retrieval", function () {
// selected with no arguments should give the current
// selection
var elements;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
elements = controller.getElements();
controller.select(elements[1]);
mockScope.selection.get.andReturn(elements[1]);
expect(controller.selected()).toEqual(elements[1]);
});
elements[1].remove();
testModel.modified = 2;
it("allows selections to be cleared", function () {
var elements;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
expect($element[0].click).toHaveBeenCalled();
elements = controller.getElements();
controller.select(elements[1]);
controller.clearSelection();
expect(controller.selected(elements[1])).toBeFalsy();
});
it("retains selections during refresh", function () {
@@ -374,21 +352,23 @@ define(
findWatch("model.modified")(testModel.modified);
elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
controller.select(elements[1]);
expect(controller.getSelectedElement()).toEqual(elements[1]);
// Verify precondition
expect(mockScope.selection.select.calls.length).toEqual(1);
// Mimic selection behavior
mockScope.selection.get.andReturn(elements[1]);
elements[2].remove();
testModel.modified = 2;
findWatch("model.modified")(testModel.modified);
elements = controller.getElements();
// Verify removal, as test assumes this
expect(elements.length).toEqual(2);
expect(controller.shouldSelect(elements[1])).toBe(true);
expect(mockScope.selection.select.calls.length).toEqual(2);
});
it("Displays received values for telemetry elements", function () {
@@ -525,25 +505,21 @@ define(
});
it("exposes a view-level selection proxy", function () {
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var selection = mockOpenMCT.selection.select.mostRecentCall.args[0];
expect(mockOpenMCT.selection.select).toHaveBeenCalled();
expect(selection.context.viewProxy).toBeDefined();
expect(mockScope.selection.proxy).toHaveBeenCalledWith(
jasmine.any(Object)
);
});
it("exposes drag handles", function () {
var handles;
// Select something so that drag handles are expected
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
controller.select(controller.getElements()[1]);
// Should have a non-empty array of handles
handles = controller.handles();
expect(handles).toEqual(jasmine.any(Array));
expect(handles.length).not.toEqual(0);
@@ -556,14 +532,15 @@ define(
});
it("exposes a move handle", function () {
var handle;
// Select something so that drag handles are expected
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
controller.select(controller.getElements()[1]);
// Should have a move handle
var handle = controller.moveHandle();
handle = controller.moveHandle();
// And it should have start/continue/end drag methods
expect(handle.startDrag).toEqual(jasmine.any(Function));
@@ -574,40 +551,26 @@ define(
it("updates selection style during drag", function () {
var oldStyle;
// Select something so that drag handles are expected
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
controller.select(controller.getElements()[1]);
mockScope.selection.get.andReturn(controller.getElements()[1]);
// Get style
oldStyle = controller.getSelectedElementStyle();
oldStyle = controller.selected().style;
// Start a drag gesture
controller.moveHandle().startDrag();
// Haven't moved yet; style shouldn't have updated yet
expect(controller.getSelectedElementStyle()).toEqual(oldStyle);
expect(controller.selected().style).toEqual(oldStyle);
// Drag a little
controller.moveHandle().continueDrag([1000, 100]);
// Style should have been updated
expect(controller.getSelectedElementStyle()).not.toEqual(oldStyle);
});
it("cleans up slection on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.mostRecentCall.args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
expect(controller.selected().style).not.toEqual(oldStyle);
});
describe("on display bounds changes", function () {
@@ -690,39 +653,6 @@ define(
});
});
it("selects an range value to display, if available", function () {
mockMetadata.valuesForHints.andReturn([
{
key: 'range',
source: 'range'
}
]);
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('range');
});
it("selects the first non-domain value to display, if no range available", function () {
mockMetadata.valuesForHints.andReturn([]);
mockMetadata.values.andReturn([
{
key: 'domain',
source: 'domain',
hints: {
domain: 1
}
},
{
key: 'image',
source: 'image',
hints: {
image: 1
}
}
]);
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('image');
});
it("reflects limit status", function () {
mockLimitEvaluator.evaluate.andReturn({cssClass: "alarm-a"});
controller.updateView(mockTelemetryObject, [{
@@ -739,14 +669,6 @@ define(
expect(controller.getElements()[0].cssClass).toEqual("alarm-a");
});
});
it("listens for selection change events", function () {
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
});
});
}

View File

@@ -21,14 +21,8 @@
*****************************************************************************/
define(
[
"../src/LayoutController",
"zepto"
],
function (
LayoutController,
$
) {
["../src/LayoutController"],
function (LayoutController) {
describe("The Layout controller", function () {
var mockScope,
@@ -38,12 +32,7 @@ define(
controller,
mockCompositionCapability,
mockComposition,
mockCompositionObjects,
mockOpenMCT,
mockSelection,
mockDomainObjectCapability,
$element = [],
selectable = [];
mockCompositionObjects;
function mockPromise(value) {
return {
@@ -69,18 +58,21 @@ define(
} else {
return {};
}
},
getCapability: function () {
return mockDomainObjectCapability;
},
hasCapability: function (param) {
if (param === 'composition') {
return id !== 'b';
}
}
};
}
// Utility function to find a watch for a given expression
function findWatch(expr) {
var watch;
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
watch = call.args[1];
}
});
return watch;
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
@@ -96,6 +88,7 @@ define(
mockComposition = ["a", "b", "c"];
mockCompositionObjects = mockComposition.map(mockDomainObject);
testConfiguration = {
panels: {
a: {
@@ -104,70 +97,27 @@ define(
}
}
};
mockDomainObjectCapability = jasmine.createSpyObj('capability',
['inEditContext']
);
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockScope.domainObject = mockDomainObject("mockDomainObject");
mockScope.model = testModel;
mockScope.configuration = testConfiguration;
selectable[0] = {
context: {
oldItem: mockScope.domainObject
}
};
mockSelection = jasmine.createSpyObj("selection", [
'select',
'on',
'off',
'get'
]);
mockSelection.get.andReturn(selectable);
mockOpenMCT = {
selection: mockSelection
};
$element = $('<div></div>');
$(document).find('body').append($element);
spyOn($element[0], 'click');
mockScope.selection = jasmine.createSpyObj(
'selection',
['select', 'get', 'selected', 'deselect']
);
spyOn(mockScope.domainObject, "useCapability").andCallThrough();
controller = new LayoutController(mockScope, $element, mockOpenMCT);
controller = new LayoutController(mockScope);
spyOn(controller, "layoutPanels").andCallThrough();
findWatch("selection")(mockScope.selection);
jasmine.Clock.useMock();
});
afterEach(function () {
$element.remove();
});
it("listens for selection change events", function () {
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
it("cleans up on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.calls[0].args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
// Model changes will indicate that panel positions
// may have changed, for instance.
it("watches for changes to composition", function () {
@@ -370,35 +320,67 @@ define(
.not.toEqual(oldStyle);
});
it("allows objects to be selected", function () {
it("allows panels to be selected", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
controller.select(mockEvent, childObj.getId());
expect(mockEvent.stopPropagation).toHaveBeenCalled();
expect(controller.selected(childObj)).toBe(true);
});
it("prevents event bubbling while drag is in progress", function () {
it("allows selection to be cleared", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
controller.select(null, childObj.getId());
controller.clearSelection();
expect(controller.selected(childObj)).toBeFalsy();
});
it("prevents clearing selection while drag is in progress", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
var id = childObj.getId();
controller.select(mockEvent, id);
// Do a drag
controller.startDrag(childObj.getId(), [1, 1], [0, 0]);
controller.startDrag(id, [1, 1], [0, 0]);
controller.continueDrag([100, 100]);
controller.endDrag();
// Because mouse position could cause the parent object to be selected, this should be ignored.
controller.bypassSelection(mockEvent);
// Because mouse position could cause clearSelection to be called, this should be ignored.
controller.clearSelection();
expect(mockEvent.stopPropagation).toHaveBeenCalled();
expect(controller.selected(childObj)).toBe(true);
// Shoud be able to select another object when dragging is done.
// Shoud be able to clear the selection after dragging is done.
jasmine.Clock.tick(0);
mockEvent.stopPropagation.reset();
controller.bypassSelection(mockEvent);
controller.clearSelection();
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
expect(controller.selected(childObj)).toBe(false);
});
it("clears selection after moving/resizing", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
var id = childObj.getId();
controller.select(mockEvent, id);
// Do a drag
controller.startDrag(id, [1, 1], [0, 0]);
controller.continueDrag([100, 100]);
controller.endDrag();
jasmine.Clock.tick(0);
controller.clearSelection();
expect(controller.selected(childObj)).toBe(false);
});
it("shows frames by default", function () {
@@ -416,74 +398,32 @@ define(
it("hides frame when selected object has frame ", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var toolbarObj = controller.getToolbar(childObj.getId(), childObj);
controller.select(mockEvent, childObj.getId());
expect(mockScope.selection.select).toHaveBeenCalled();
var selectedObj = mockScope.selection.select.mostRecentCall.args[0];
expect(controller.hasFrame(childObj)).toBe(true);
expect(toolbarObj.hideFrame).toBeDefined();
expect(toolbarObj.hideFrame).toEqual(jasmine.any(Function));
expect(selectedObj.hideFrame).toBeDefined();
expect(selectedObj.hideFrame).toEqual(jasmine.any(Function));
});
it("shows frame when selected object has no frame", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[1];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var toolbarObj = controller.getToolbar(childObj.getId(), childObj);
controller.select(mockEvent, childObj.getId());
expect(mockScope.selection.select).toHaveBeenCalled();
var selectedObj = mockScope.selection.select.mostRecentCall.args[0];
expect(controller.hasFrame(childObj)).toBe(false);
expect(toolbarObj.showFrame).toBeDefined();
expect(toolbarObj.showFrame).toEqual(jasmine.any(Function));
expect(selectedObj.showFrame).toBeDefined();
expect(selectedObj.showFrame).toEqual(jasmine.any(Function));
});
it("selects the parent object when selected object is removed", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var composition = ["b", "c"];
mockScope.$watchCollection.mostRecentCall.args[1](composition);
expect($element[0].click).toHaveBeenCalled();
});
it("allows objects to be drilled-in only when editing", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
childObj.getCapability().inEditContext.andReturn(false);
controller.drill(mockEvent, childObj);
expect(controller.isDrilledIn(childObj)).toBe(false);
});
it("allows objects to be drilled-in only if it has sub objects", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[1];
childObj.getCapability().inEditContext.andReturn(true);
controller.drill(mockEvent, childObj);
expect(controller.isDrilledIn(childObj)).toBe(false);
});
it("selects a newly-dropped object", function () {
mockScope.$on.mostRecentCall.args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
var childObj = mockDomainObject("d");
var testElement = $("<div data-layout-id='some-id'></div>");
$element.append(testElement);
spyOn(testElement[0], 'click');
controller.selectIfNew('some-id', childObj);
jasmine.Clock.tick(0);
expect(testElement[0].click).toHaveBeenCalled();
});
});
}
);

View File

@@ -49,7 +49,7 @@
{
"key": "ListViewController",
"implementation": ListViewController,
"depends": ["$scope", "formatService"]
"depends": ["$scope"]
}
],
"directives": [

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define(function () {
function ListViewController($scope, formatService) {
function ListViewController($scope) {
this.$scope = $scope;
$scope.orderByField = 'title';
$scope.reverseSort = false;
@@ -30,8 +30,6 @@ define(function () {
var unlisten = $scope.domainObject.getCapability('mutation')
.listen(this.updateView.bind(this));
this.utc = formatService.getFormat('utc');
$scope.$on('$destroy', function () {
unlisten();
});
@@ -52,13 +50,17 @@ define(function () {
icon: child.getCapability('type').getCssClass(),
title: child.getModel().name,
type: child.getCapability('type').getName(),
persisted: this.utc.format(child.getModel().persisted),
modified: this.utc.format(child.getModel().modified),
persisted: new Date(
child.getModel().persisted
).toUTCString(),
modified: new Date(
child.getModel().modified
).toUTCString(),
asDomainObject: child,
location: child.getCapability('location'),
action: child.getCapability('action')
};
}, this);
});
};
return ListViewController;

View File

@@ -31,9 +31,7 @@ define(
controller,
childModel,
typeCapability,
mutationCapability,
formatService;
mutationCapability;
beforeEach(function () {
unlistenFunc = jasmine.createSpy("unlisten");
@@ -43,18 +41,6 @@ define(
);
mutationCapability.listen.andReturn(unlistenFunc);
formatService = jasmine.createSpyObj(
"formatService",
["getFormat"]
);
formatService.getFormat.andReturn(jasmine.createSpyObj(
'utc',
["format"]
));
formatService.getFormat().format.andCallFake(function (v) {
return "formatted " + v;
});
typeCapability = jasmine.createSpyObj(
"typeCapability",
["getCssClass", "getName"]
@@ -108,27 +94,20 @@ define(
);
scope.domainObject = domainObject;
controller = new ListViewController(scope, formatService);
controller = new ListViewController(scope);
waitsFor(function () {
return scope.children;
});
});
it("uses the UTC time format", function () {
expect(formatService.getFormat).toHaveBeenCalledWith('utc');
});
it("updates the view", function () {
expect(scope.children[0]).toEqual(
{
icon: "icon-folder",
title: "Battery Charge Status",
type: "Folder",
persisted: formatService.getFormat('utc')
.format(childModel.persisted),
modified: formatService.getFormat('utc')
.format(childModel.modified),
persisted: "Wed, 07 Jun 2017 20:34:57 GMT",
modified: "Wed, 07 Jun 2017 20:34:57 GMT",
asDomainObject: childObject,
location: ''
}

View File

@@ -1,326 +0,0 @@
define([
"legacyRegistry",
"./src/controllers/NotebookController",
"./src/controllers/NewEntryController",
"./src/controllers/SelectSnapshotController",
"./src/controllers/LayoutNotebookController",
"./src/directives/MCTSnapshot",
"./src/directives/MCTModalNotebook",
"./src/directives/EntryDnd",
"./src/actions/ViewSnapshot",
"./src/actions/AnnotateSnapshot",
"./src/actions/RemoveEmbed",
"./src/actions/CreateSnapshot",
"./src/actions/RemoveSnapshot",
"./src/actions/NewEntryContextual",
"./src/capabilities/NotebookCapability",
"./src/policies/CompositionPolicy",
"./src/policies/ViewPolicy"
], function (
legacyRegistry,
NotebookController,
NewEntryController,
SelectSnapshotController,
LayoutNotebookController,
MCTSnapshot,
MCTModalNotebook,
MCTEntryDnd,
ViewSnapshotAction,
AnnotateSnapshotAction,
RemoveEmbedAction,
CreateSnapshotAction,
RemoveSnapshotAction,
newEntryAction,
NotebookCapability,
CompositionPolicy,
ViewPolicy
) {
legacyRegistry.register("platform/features/notebook", {
"name": "Notebook Plugin",
"description": "Create and save timestamped notes with embedded object snapshots.",
"extensions":
{
"types": [
{
"key": "notebook",
"name": "Notebook",
"cssClass": "icon-notebook",
"description": "Create and save timestamped notes with embedded object snapshots.",
"features": ["creation"],
"model": {
"entries": [],
"composition": [],
"entryTypes": []
}
}
],
"views": [
{
"key": "notebook.view",
"type": "notebook",
"cssClass": "icon-notebook",
"name": "notebook",
"templateUrl": "templates/notebook.html",
"editable": false,
"uses": [
"composition",
"action"
],
"gestures": [
"drop"
]
},
{
"key": "layoutNotebook",
"name": "Display Layout",
"cssClass": "icon-layout",
"type": "layout",
"templateUrl": "templates/layoutNotebook.html",
"editable": true,
"uses": [],
"toolbar": {
"sections": [
{
"items": [
{
"method": "showFrame",
"cssClass": "icon-frame-show",
"control": "button",
"title": "Show frame",
"description": "Show frame"
},
{
"method": "hideFrame",
"cssClass": "icon-frame-hide",
"control": "button",
"title": "Hide frame",
"description": "Hide frame"
}
]
}
]
}
}
],
"controllers": [
{
"key": "NotebookController",
"implementation": NotebookController,
"depends": ["$scope",
"dialogService",
"popupService",
"agentService",
"objectService",
"navigationService",
"now",
"actionService",
"$timeout",
"$element"
]
},
{
"key": "NewEntryController",
"implementation": NewEntryController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "selectSnapshotController",
"implementation": SelectSnapshotController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "LayoutNotebookController",
"implementation": LayoutNotebookController,
"depends": ["$scope"]
}
],
"representations": [
{
"key": "draggedEntry",
"templateUrl": "templates/entry.html"
},
{
"key": "frameLayoutNotebook",
"templateUrl": "templates/frameLayoutNotebook.html"
}
],
"templates": [
{
"key": "annotate-snapshot",
"templateUrl": "templates/annotation.html"
},
{
"key": "notificationTemplate",
"templateUrl": "templates/notifications.html"
}
],
"directives": [
{
"key": "mctSnapshot",
"implementation": MCTSnapshot,
"depends": [
"$rootScope",
"$document",
"exportImageService",
"dialogService",
"notificationService"
]
},
{
"key": "mctEntryDnd",
"implementation": MCTEntryDnd,
"depends": [
"$rootScope",
"$compile",
"dndService",
"typeService",
"notificationService"
]
},
{
"key": "mctModalNotebook",
"implementation": MCTModalNotebook,
"depends": [
"$document"
]
}
],
"actions": [
{
"key": "view-snapshot",
"implementation": ViewSnapshotAction,
"name": "View Snapshot",
"description": "View the large image in a modal",
"category": "embed",
"depends": [
"$compile"
]
},
{
"key": "annotate-snapshot",
"implementation": AnnotateSnapshotAction,
"name": "Annotate Snapshot",
"cssClass": "icon-pencil labeled",
"description": "Annotate embed's snapshot",
"category": "embed",
"depends": [
"dialogService",
"dndService",
"$rootScope"
]
},
{
"key": "remove-embed",
"implementation": RemoveEmbedAction,
"name": "Remove...",
"cssClass": "icon-trash labeled",
"description": "Remove this embed",
"category": [
"embed",
"embed-no-snap"
],
"depends": [
"dialogService"
]
},
{
"key": "remove-snapshot",
"implementation": RemoveSnapshotAction,
"name": "Remove Snapshot",
"cssClass": "icon-trash labeled",
"description": "Remove Snapshot of the embed",
"category": "embed",
"depends": [
"dialogService"
]
},
{
"key": "create-snapshot",
"implementation": CreateSnapshotAction,
"name": "Create Snapshot",
"description": "Create a snapshot for the embed",
"category": "embed-no-snap",
"priority": "preferred",
"depends": [
"$compile"
]
},
{
"key": "notebook-new-entry",
"implementation": newEntryAction,
"name": "New Notebook Entry",
"cssClass": "icon-notebook labeled",
"description": "Add a new entry",
"category": [
"contextual",
"view-control"
],
"depends": [
"$compile",
"$rootScope",
"dialogService",
"notificationService",
"linkService"
],
"priority": "preferred"
}
],
"licenses": [
{
"name": "painterro",
"version": "4.1.0",
"author": "Mike Bostock",
"description": "D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.",
"website": "https://d3js.org/",
"copyright": "Copyright 2010-2016 Mike Bostock",
"license": "BSD-3-Clause",
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
],
"capabilities": [
{
"key": "notebook",
"name": "Notebook Capability",
"description": "Provides a capability for looking for a notebook domain object",
"implementation": NotebookCapability,
"depends": [
"typeService"
]
}
],
"policies": [
{
"category": "composition",
"implementation": CompositionPolicy,
"message": "Objects of this type cannot contain objects of that type."
},
{
"category": "view",
"implementation": ViewPolicy
}
],
"controls": [
{
"key": "embed-control",
"templateUrl": "templates/controls/embedControl.html"
},
{
"key": "snapshot-select",
"templateUrl": "templates/controls/snapSelect.html"
}
],
"stylesheets": [
{
"stylesheetUrl": "css/notebook.css",
"theme": "espresso"
}
]
}
});
});

View File

@@ -1,414 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-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.
*****************************************************************************/
.mct-notebook{
overflow: hidden;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: auto;
height: auto;
div.example-button-group {
margin-top: 12px;
margin-bottom: 12px;
}
.example-button-group a {
padding: 3px;
margin: 3px;
}
.example-button-group a.selected {
border: 1px gray solid;
border-radius: 3px;
background: #444;
}
.example-task-completed .example-task-description {
text-decoration: line-through;
opacity: 0.75;
}
.example-task-description.selected {
background: #46A;
border-radius: 3px;
}
.example-message {
font-style: italic;
}
}
.notebook-new-entry{
height: 350px;
}
.drag-area{
border: 2px dashed #006080;
border-radius: 5px;
padding: 10px 0;
margin: 15px 0;
font-style: italic;
cursor: pointer;
&.drag-active{
border: 2px dashed $colorKey;
}
.glyph{
flex: 1 1 0;
&:before{
left:10px;
position: relative;
}
}
p{
flex: 4 1 0;
margin: 0;
}
}
.frame{
.icon-notebook{
margin-right: 5px;
}
}
.overlay.l-dialog .title{
white-space: normal;
}
.entries-wrapper{
display: block;
position: relative;
overflow: scroll;
height: calc(100% - 92px);
.entries-list{
.entry{
border-bottom: 1px solid #999;
padding: 10px 0;
&:first-child{
border-top: 1px solid #999;
}
&.active,&:hover{
background-color: rgba(0, 153, 204, 0.2);
.entry-info .entry-text{
.unedited-text{
display: none;
}
textarea{
background-color: $colorBodyBg;
min-height: 50px;
height: auto;
resize: none;
display: block;
}
}
}
.entry-time{
flex: 1 1 0;
text-align: center;
}
.entry-info{
flex: 4 1 0;
padding: 0 15px;
min-height: 65px;
.embeds{
flex-wrap: wrap;
}
.snap-thumb,.embed-icon{
cursor: pointer;
}
.entry-text{
min-height: 50px;
margin-bottom:15px;
display: block;
overflow: hidden;
.unedited-text{
margin: 0;
min-height: 50px;
p{
margin: 0;
}
}
textarea{
display: none;
}
}
.entry-embedded{
margin-right: 20px;
height: 100px;
position: relative;
margin-bottom: 20px
}
}
.entry-delete{
padding: 0 15px;
}
}
}
}
.embed{
.embed-info{
.embed-title{
z-index: 99;
text-transform: uppercase;
max-width: 200px;
}
.embed-date{
font-size: 14px;
margin-top: 5px;
}
}
.embed-icon{
margin: 10px;
position: absolute;
}
.btn-annotate{
margin: 0 15px;
z-index: 3;
}
&.nosnapshot{
.embed-icon{
margin: 2px;
cursor: initial;
}
.embed-info{
padding-left: 25px;
}
.embed-title{
max-width: 200px;
}
}
}
.snapshot{
z-index: -9999;
background: $colorBodyBg;
}
.t-contents,.snap-annotation{
overflow: hidden;
img{
width: 100%;
}
}
.snap-thumb{
width: 100px;
height: 100px;
border-radius: 5px;
overflow: hidden;
border: 1px solid #999;
margin-right: 15px;
img{
height: 100%;
width: 100%;
}
}
.notebook-filters{
.select{
margin-left: 10px;
border-radius: 5px;
}
}
.embed-info{
.embed-title{
text-transform: uppercase;
font-weight: bold;
}
}
.user-folders{
border-top: 1px solid #999;
padding-top: 10px;
.t-item-icon{
margin-right: 10px;
}
}
.options-menu-container{
margin: 0;
padding: 0;
display: block;
.embed-option{
box-sizing: border-box;
border-top: 1px solid #878787;
color: white;
line-height: 1.5rem;
padding: 3px 10px 3px 28px;
position: relative;
white-space: nowrap;
display: list-item;
&:first-child{
border: none;
}
}
}
.new-display-embed{
font-size: 14px;
.nosnapshot{
.embed-icon{
margin: 2px;
}
.embed-info{
padding-left: 25px;
}
}
}
.view-header{
.view-date{
float: right;
}
.view-snap-info{
float: left;
}
.s-button{
clear: both;
float: left;
margin-top: 10px;
font-size: 14px;
}
.view-info{
float: left;
.embed-icon{
display: inline-block;
}
.embed-title{
display: inline-block;
margin: 0 5px 0 10px;
}
.object-header{
display: inline-block;
}
}
}
.context-available{
outline: none;
}
.menu-element.menu-view{
z-index: 999;
}
.new-notebook-entry{
float: left;
.title-label{
margin-left: 5px;
}
}
.notebook-button-container{
position: absolute;
top:30px;
}
.container-notebook{
top:64px;
height: calc(100% - 30px);
}
.ptro-color-main{
top: 0;
z-index: 10;
position: relative;
height: auto;
min-height: 41px;
}
.ptro-wrapper{
bottom: 0;
top: 40px;
}
.overlay.l-dialog .abs.editor{
padding-right: 0;
}
.overlay.l-dialog .outer-holder.annotation-dialog{
width: 90%;
height: 90%;
}
.snap-annotation-wrapper{
padding-top: 40px;
}
.annotate-new{
.s-button{
margin-left: 25px;
margin-top: 10px;
z-index: 9;
}
}
@media screen and (max-width: 1024px){
.entries-wrapper{
font-size: 14px;
}
.l-message{
display: inline-block;
width: 100%;
.type-icon.message-type{
margin: 0 auto 40px;
}
.top-bar .title{
text-align: center;
white-space: normal;
}
}
.ptro-color-main{
height: 80px;
.tool-controls,>div>span:not(.ptro-info){
float: left;
}
}
.ptro-wrapper{
top: 80px;
}
}
@media screen and (max-width: 768px){
.ptro-bar>div {
white-space: normal;
}
}

View File

@@ -1,37 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2015, 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.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../platform/commonUI/general/res/sass/constants";
@import "../../../../platform/commonUI/general/res/sass/mixins";
@import "../../../../platform/commonUI/themes/espresso/res/sass/constants";
@import "../../../../platform/commonUI/themes/espresso/res/sass/mixins";
// Thematic constants
$colorCode: rgba(black, 0.2);
$colorGlyphExample: #fff;
@import "notebook-base";
div.themed.espresso { display: block; }
span.themed.espresso { display: inline; }

View File

@@ -1,2 +0,0 @@
<div class="snap-annotation" id="snap-annotation" ng-init="ngModel.tracker()">
</div>

View File

@@ -1,57 +0,0 @@
<!--
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.
-->
<div class='form-control'>
<ng-form name="mctControl">
<div class='fields' ng-controller="NewEntryController">
<div class="l-flex-row new-display-embed">
<div class="l-flex-row embed">
<div ng-if="snapToggle" class="l-flex-row">
<div class="snap-thumb l-flex-row">
<div ng-class="cssClass" class="abs embed-icon"></div>
<img ng-src="{{snapshot.src}}" alt="{{snapshot.modified}}" >
</div>
<div class="embed-info l-flex-col">
<div class="embed-title">{{objectName}}</div>
<div class="embed-date">{{snapshot.modified| date:'yyyy-MM-dd HH:mm:ss'}}</div>
</div>
</div>
<div ng-if="!snapToggle" class="l-flex-row nosnapshot">
<div class="l-flex-row">
<span ng-class="cssClass" class="abs embed-icon"></span>
<div class="embed-info">
<span class="embed-title">{{objectName}}</span>
<span class="embed-date">{{snapshot.modified| date:'yyyy-MM-dd HH:mm:ss'}}
</span>
</div>
</div>
</div>
</div>
<div class="l-flex-row embed annotate-new" ng-if="snapToggle">
<a class="s-button icon-pencil" title="Edit" ng-click="annotateSnapshot()">
<span class="title-label">Annotate</span>
</a>
</div>
</div>
</div>
</ng-form>
</div>

View File

@@ -1,30 +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 class='form-control select' ng-controller="selectSnapshotController">
<select
ng-model="selectModel"
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
name="mctControl">
<!-- <option value="" ng-show="!ngModel[field]">- Select One -</option> -->
</select>
</div>

View File

@@ -1,38 +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 class="frame snap-frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
</div>
</div>
<div class="abs object-holder" data-entry = "{{parameters.entry}}" data-embed = "{{parameters.embed}}" mct-snapshot ng-if="representation.selected.key">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -1,54 +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 class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header-frame'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
<a class="s-button icon-notebook t-btn-view-large"
title="New Notebook Entry"
ng-if="parameters"
ng-click="ngModel()">
</a>
<a class="s-button icon-expand t-btn-view-large"
title="View large"
mct-modal-notebook>
</a>
</div>
</div>
<div class="abs object-holder">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -1,84 +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 class="abs l-layout"
ng-controller="LayoutController as controller"
ng-click="controller.clearSelection()">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
ng-repeat="childObject in composition"
ng-click="controller.select($event, childObject.getId())"
ng-style="controller.getFrameStyle(childObject.getId())">
<div ng-controller="LayoutNotebookController as controller">
<mct-representation key="'frameLayoutNotebook'"
class="t-rep-frame holder contents abs"
parameters = "hasNotebookAction"
ng-model="newNotebook"
mct-object="childObject">
</mct-representation>
</div>
<!-- Drag handles -->
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
<span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-nw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-ne"
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-sw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-se"
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
</span>
</div>
</div>

View File

@@ -1,122 +0,0 @@
<div ng-controller="NotebookController as controller" class="mct-notebook">
<!-- Reuse of native styles the more as I can -->
<div class="notebook-head l-flex-row flex-elem grows">
<div class="l-flex-row holder holder-search">
<div class="search-bar flex-elem l-flex-row"
ng-class="{ holder: !(entrySearch === '' || entrySearch === undefined) }">
<div class="holder flex-elem grows">
<input class="search-input"
type="text" tabindex="10000"
ng-model="entrySearch"
ng-keyup="controller.search()"/>
<a class="clear-icon clear-input icon-x-in-circle"
ng-class="{show: !(entrySearch === '' || entrySearch === undefined)}"
ng-click="entrySearch = ''; controller.search()"></a>
</div>
<a class="holder s-button flex-elem btn-cancel"
ng-show="!(entrySearch === '' || entrySearch === undefined)"
ng-click="entrySearch = ''; ngModel.checkAll = true; menuController.checkAll(); controller.search()">
Cancel</a>
</div>
</div>
<div class="notebook-filters right l-flex-row flex-elem grows flex-justify-end">
<div class="select">
<select ng-model="showTime">
<option value="0" selected="selected">Show all</option>
<option value="1">Last Hour</option>
<option value="8">Last 8 Hours</option>
<option value="24">Last 24 Hours</option>
</select>
</div>
<div class="select">
<select ng-model="sortEntries">
<option value="-createdOn" selected="selected">Newest first</option>
<option value="createdOn">Oldest first</option>
</select>
</div>
</div>
</div>
<!-- drag area -->
<div class="drag-area l-flex-row" ng-click="newEntry($event)" id="newEntry" mct-entry-dnd>
<div class="glyph icon-plus" title="new"></div>
<p>To start a new entry, click here or drag and drop any object</p>
</div>
<!-- entries -->
<div class="entries-wrapper" ng-mouseover="handleActive()">
<ul class="entries-list">
<li class="entry l-flex-row" id="{{'entry_'+entry.createdOn}}" ng-if="hoursFilter(showTime,entry.createdOn)" ng-repeat="entry in model.entries | filter:entrySearch | orderBy: sortEntries track by $index" ng-init="$last && finished(model.entries)" mct-entry-dnd>
<div class="entry-time">{{entry.createdOn | date:'yyyy-MM-dd HH:mm:ss'}}</div>
<div class="entry-info">
<div class="entry-text">
<div class="unedited-text">
<p ng-repeat="line in parseText(entry.text) track by $index">
{{line}}
</p>
</div>
<textarea ng-blur="textBlur($event,entry.createdOn)" ng-focus="textFocus($event)" ng-model="entry.text" placeholder="Enter text here" class="sm" rows="{{parseText(entry.text).length}}">{{entry.text}}</textarea>
</div>
<!-- embeds -->
<div class="embeds l-flex-row">
<div class="entry-embedded" ng-repeat="embed in entry.embeds track by $index" id="{{embed.id}}">
<div class="l-flex-row embed" ng-class="{ 'nosnapshot' : !embed.snapshot}">
<div ng-class="embed.cssClass" class="embed-icon" ng-click="viewSnapshot($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)"></div>
<div class="snap-thumb" ng-if="embed.snapshot" ng-click="viewSnapshot($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)">
<img ng-src="{{embed.snapshot.src}}" src="//:0" alt="{{embed.id}}" >
</div>
<div class="embed-info l-flex-col">
<div class="embed-title object-header">
<a href="#" ng-click='navigate($event,embed.type)'>{{embed.name}}</a>
<a href="#" class='context-available' ng-click='openMenu($event,embed.type)'></a>
</div>
<div class="hide-menu" ng-show="false">
<div class="menu-element context-menu-wrapper mobile-disable-select">
<div class="menu context-menu">
<ul>
<li ng-repeat="menu in menuEmbed"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in menuEmbedNoSnap"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="!embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in embedActions"
ng-click="menu.perform()"
title="{{menu.name}}"
class="{{menu.cssClass}}">
{{menu.name}}
</li>
</ul>
</div>
</div>
</div>
<div class="embed-date">{{embed.id| date:'yyyy-MM-dd HH:mm:ss'}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- delete entry -->
<div class="entry-delete">
<a class="s-icon-button icon-trash" title="Delete Entry" ng-click="deleteEntry($event)"></a>
</div>
</li>
</ul>
</div>
</div>

View File

@@ -1,8 +0,0 @@
<span class="status block">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<span class="status-indicator icon-bell"></span>
<span class="label">
Notifications
</span>
<span class="count"></span>
</span>

View File

@@ -1,111 +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 viewSnapshot (Originally NewWindowAction). Created by vwoeltje on 11/18/14.
*/
define(
["painterro", "zepto"],
function (Painterro, $) {
var ANNOTATION_STRUCT = {
title: "Annotate Snapshot",
template: "annotate-snapshot",
options: [{
name: "OK",
key: "ok",
description: "save annotation"
},
{
name: "Cancel",
key: "cancel",
description: "cancel editing"
}]
};
function AnnotateSnapshot(dialogService,dndService,$rootScope,context) {
context = context || {};
// Choose the object to be opened into a new tab
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
this.dndService = dndService;
this.$rootScope = $rootScope;
}
AnnotateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var DOMAIN_OBJECT = this.domainObject;
var ROOTSCOPE = this.$rootScope;
this.dialogService.getUserChoice(ANNOTATION_STRUCT)
.then(saveNotes);
var painterro;
var tracker = function () {
$(document.body).find('.l-dialog .outer-holder').addClass('annotation-dialog');
painterro = Painterro({
id: 'snap-annotation',
backgroundFillColor: '#eee',
hiddenTools: ['save', 'open', 'close','eraser'],
saveHandler: function (image, done) {
if (entryId && embedId) {
var elementPos = DOMAIN_OBJECT.model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = DOMAIN_OBJECT.model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
$scope.saveSnap(image.asBlob(), embedPos, elementPos);
}else {
ROOTSCOPE.snapshot = {'src': image.asDataURL('image/png'),
'modified': Date.now()};
}
done(true);
}
}).show(snapshot);
};
ANNOTATION_STRUCT.model = {'tracker': tracker};
function saveNotes(param) {
if (param === 'ok') {
painterro.save();
}else {
ROOTSCOPE.snapshot = "annotationCancelled";
}
}
};
return AnnotateSnapshot;
}
);

View File

@@ -1,65 +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 SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function CreateSnapshot($compile,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.context = context;
this.$compile = $compile;
}
CreateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var compile = this.$compile;
var model = this.domainObject.model;
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
var embedType = entryEmbeds[embedPos].type;
$scope.getDomainObj(embedType).then(function (resp) {
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = resp[embedType];
$scope.entryId = elementPos;
$scope.embedId = embedPos;
compile(SNAPSHOT_TEMPLATE)($scope);
}
});
};
return CreateSnapshot;
}
);

View File

@@ -1,191 +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 SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
var NEW_TASK_FORM = {
name: "Create a Notebook Entry",
hint: "Please select one Notebook",
sections: [{
rows: [{
name: 'Entry',
key: 'entry',
control: 'textarea',
required: true,
"cssClass": "l-textarea-sm"
},
{
name: 'Embed Type',
key: 'withSnapshot',
control: 'snapshot-select',
"options": [
{
"name": "Link and Snapshot",
"value": true
},
{
"name": "Link only",
"value": false
}
]
},
{
name: 'Embed',
key: 'embedObject',
control: 'embed-control'
},
{
name: 'Save in Notebook',
key: 'saveNotebook',
control: 'locator',
validate: validateLocation
}]
}]
};
function NewEntryContextual($compile,$rootScope,dialogService,notificationService,linkService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
this.linkService = linkService;
this.$rootScope = $rootScope;
this.$compile = $compile;
}
function validateLocation(newParentObj) {
return newParentObj.model.type === 'notebook';
}
NewEntryContextual.prototype.perform = function () {
var self = this;
var domainObj = this.domainObject;
var notification = this.notificationService;
var dialogService = this.dialogService;
var rootScope = this.$rootScope;
rootScope.newEntryText = '';
// Create the overlay element and add it to the document's body
this.$rootScope.selObj = domainObj;
this.$rootScope.selValue = "";
var newScope = rootScope.$new();
newScope.selObj = domainObj;
newScope.selValue = "";
this.$compile(SNAPSHOT_TEMPLATE)(newScope);
//newScope.$destroy();
this.$rootScope.$watch("snapshot", setSnapshot);
function setSnapshot(value) {
if (value === "annotationCancelled") {
rootScope.snapshot = rootScope.lastValue;
rootScope.lastValue = '';
}else if (value && value !== rootScope.lastValue) {
var overlayModel = {
title: NEW_TASK_FORM.name,
message: NEW_TASK_FORM.message,
structure: NEW_TASK_FORM,
value: {'entry': rootScope.newEntryText || ""}
};
rootScope.currentDialog = overlayModel;
dialogService.getDialogResponse(
"overlay-dialog",
overlayModel,
function () {
return overlayModel.value;
}
).then(addNewEntry);
rootScope.lastValue = value;
}
}
function addNewEntry(options) {
options.selectedModel = options.embedObject.getModel();
options.cssClass = options.embedObject.getCapability('type').typeDef.cssClass;
if (self.$rootScope.snapshot) {
options.snapshot = self.$rootScope.snapshot;
self.$rootScope.snapshot = undefined;
}else {
options.snapshot = undefined;
}
if (!options.withSnapshot) {
options.snapshot = '';
}
createSnap(options);
}
function createSnap(options) {
options.saveNotebook.useCapability('mutation', function (model) {
var entries = model.entries;
var lastEntry = entries[entries.length - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
model.entries.push({
'createdOn': Date.now(),
'text': options.entry,
'embeds': [{'type': options.embedObject.getId(),
'id': '' + Date.now(),
'cssClass': options.cssClass,
'name': options.selectedModel.name,
'snapshot': options.snapshot
}]
});
}else {
model.entries[entries.length - 1] = {
'createdOn': Date.now(),
'text': options.entry,
'embeds': [{'type': options.embedObject.getId(),
'id': '' + Date.now(),
'cssClass': options.cssClass,
'name': options.selectedModel.name,
'snapshot': options.snapshot
}]
};
}
});
notification.info({
title: "Notebook Entry created"
});
}
};
NewEntryContextual.appliesTo = function (context) {
var domainObject = context.domainObject;
return domainObject && domainObject.hasCapability("notebook") &&
domainObject.getCapability("notebook").isNotebook();
};
return NewEntryContextual;
}
);

View File

@@ -1,72 +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 () {
function RemoveEmbed(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveEmbed.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Embed. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds.splice(embedPos, 1);
});
}
};
return RemoveEmbed;
}
);

View File

@@ -1,74 +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 () {
function RemoveSnapshot(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Snapshot. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds[embedPos].snapshot = "";
});
}
};
return RemoveSnapshot;
}
);

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 ViewSnapshot
*/
define(
['zepto'],
function ($) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
var toggleOverlay,
overlay,
closeButton,
doneButton,
blocker,
overlayContainer,
img,
annotateButton,
annotateImg;
function ViewSnapshot($compile,context) {
context = context || {};
this.$compile = $compile;
}
function openOverlay(url,header) {
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
annotateButton = header.querySelector('a.icon-pencil');
annotateButton.addEventListener('click', annotateImg);
document.body.appendChild(overlay);
img = document.createElement('img');
img.src = url;
overlayContainer.appendChild(header);
overlayContainer.appendChild(img);
}
function closeOverlay() {
overlayContainer.removeChild(img);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
img = undefined;
}
function headerTemplate() {
var template = '<div class="view-header">' +
'<div class="view-info">' +
'<div ng-class="cssClass" class="embed-icon"></div>' +
'<div class="embed-title">{{entryName}}</div>' +
'<div class="object-header">' +
'<a href="" class="context-available" ng-click="openMenu($event,embedType)""></a>' +
'</div>' +
'<div class="hide-menu" ng-show="false">' +
'<div class="menu-element menu-view context-menu-wrapper mobile-disable-select">' +
'<div class="menu context-menu">' +
'<ul>' +
'<li ng-repeat="menu in embedActions"' +
'ng-click="menu.perform()"' +
'title="{{menu.name}}"' +
'class="{{menu.cssClass}}">' +
'{{menu.name}}' +
'</li>' +
'</ul>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="view-date">' +
'<span class="icon-alert-rect" title="Snapshot">' +
'</span> ' +
'SNAPSHOT {{snapDate | date:\'yyyy-MM-dd HH:mm:ss\'}}' +
'</div>' +
'<a class="s-button icon-pencil" title="Annotate">' +
'<span class="title-label">Annotate</span>' +
'</a>' +
'</div>';
return template;
}
ViewSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope,embed) {
var isOpen = false;
// Create the overlay element and add it to the document's body
$scope.cssClass = embed.cssClass;
$scope.embedType = embed.type;
$scope.entryName = embed.name;
$scope.snapDate = +embedId;
var element = this.$compile(headerTemplate())($scope);
var annotateAction = $scope.action.getActions({category: 'embed'})[1];
toggleOverlay = function () {
if (!isOpen) {
openOverlay(snapshot, element[0]);
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
annotateImg = function () {
closeOverlay();
annotateAction.perform($event, snapshot, embedId, entryId, $scope);
};
toggleOverlay();
};
return ViewSnapshot;
}
);

View File

@@ -1,50 +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 notebook capability allows a domain object to know whether the
* notebook plugin is present or not.
*
* @constructor
*/
function NotebookCapability(typeService, domainObject) {
this.domainObject = domainObject;
this.typeService = typeService;
return this;
}
/**
* Returns true if there is a notebook domain Object.
*
* @returns {Boolean}
*/
NotebookCapability.prototype.isNotebook = function () {
return this.typeService.getType('notebook');
};
return NotebookCapability;
}
);

View File

@@ -1,54 +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 implements object types and associated views for
* display-building.
*/
define(
[],
function () {
/**
* The LayoutNotebookController is responsible for supporting the
* notebook feature creation on theLayout view.
**/
function LayoutNotebookController($scope) {
$scope.hasNotebookAction = undefined;
$scope.newNotebook = undefined;
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
$scope.hasNotebookAction = true;
$scope.newNotebook = function () {
notebookAction[0].perform();
};
}
}
return LayoutNotebookController;
}
);

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.
*****************************************************************************/
/**
* Module defining NewEntryController. */
define(
[],
function () {
function NewEntryController($scope,$rootScope) {
$scope.snapshot = undefined;
$scope.snapToggle = true;
$scope.entryText = '';
var annotateAction = $rootScope.selObj.getCapability('action').getActions(
{category: 'embed'})[1];
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = $rootScope.selObj;
$scope.objectName = $rootScope.selObj.getModel().name;
$scope.cssClass = $rootScope.selObj.getCapability('type').typeDef.cssClass;
$scope.annotateSnapshot = function ($event) {
if ($rootScope.currentDialog.value) {
$rootScope.newEntryText = $scope.$parent.$parent.ngModel.entry;
$rootScope.currentDialog.cancel();
annotateAction.perform($event, $rootScope.snapshot.src);
$rootScope.currentDialog = undefined;
}
};
function updateSnapshot(img) {
$scope.snapshot = img;
}
// Update set of actions whenever the action capability
// changes or becomes available.
$rootScope.$watch("snapshot", updateSnapshot);
$rootScope.$watch("selValue", toggleEmbed);
function toggleEmbed(value) {
$scope.snapToggle = value;
}
}
return NewEntryController;
}
);

View File

@@ -1,331 +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.
*****************************************************************************/
/*-- main controller file, here is the core functionality of the notebook plugin --*/
define(
['zepto'],
function ($) {
function NotebookController(
$scope,
dialogService,
popupService,
agentService,
objectService,
navigationService,
now,
actionService,
$timeout,
$element
) {
$scope.entriesEl = $(document.body).find('.entries-list');
$scope.sortEntries = '-createdOn';
$scope.showTime = "0";
$scope.editEntry = false;
$scope.entrySearch = '';
$scope.entryTypes = [];
$scope.embedActions = [];
$scope.currentEntryValue = '';
/*--seconds in an hour--*/
var SECONDS_IN_AN_HOUR = 60 * 60 * 1000;
this.scope = $scope;
$scope.hoursFilter = function (hours,entryTime) {
if (+hours) {
return entryTime > (now() - SECONDS_IN_AN_HOUR * (+hours));
}else {
return true;
}
};
$scope.scrollToTop = function () {
var entriesContainer = $scope.entriesEl.parent();
entriesContainer[0].scrollTop = 0;
};
/*--create a new entry--*/
$scope.newEntry = function ($event) {
$scope.scrollToTop();
var entries = $scope.domainObject.model.entries;
var lastEntry = entries[entries.length - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.push({'createdOn': now()});
});
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entries.length - 1].createdOn = now();
});
}
$scope.entrySearch = '';
};
/*--delete an entry--*/
$scope.deleteEntry = function ($event) {
var delId = $event.currentTarget.parentElement.parentElement.id;
var errorDialog = dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Notebook entry. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
var elementPos = $scope.domainObject.model.entries.map(function (x) {
return x.createdOn;
}).indexOf(+delId.replace('entry_', ''));
if (elementPos !== -1) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.splice(elementPos, 1);
});
} else {
window.console.log('delete error');
}
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
};
$scope.textFocus = function ($event) {
if ($event.currentTarget && $event.currentTarget.value) {
$scope.currentEntryValue = $event.currentTarget.value;
}
};
$scope.textBlur = function ($event,entryId) {
if ($event.target && $event.target.value !== "") {
var elementPos = $scope.domainObject.model.entries.map(function (x) {
return x.createdOn;
}).indexOf(+(entryId));
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[elementPos].text = $event.target.value;
if ($scope.currentEntryValue !== $event.target.value) {
model.entries[elementPos].createdOn = now();
}
});
}
};
$scope.finished = function (model) {
var lastEntry = model[model.length - 1];
if (!lastEntry.text) {
var newEntry = $scope.entriesEl.find('#entry_' + lastEntry.createdOn).addClass('active');
newEntry.find('textarea').focus();
}
};
$scope.handleActive = function () {
var newEntry = $scope.entriesEl.find('.active');
if (newEntry) {
newEntry.removeClass('active');
}
};
$scope.clearSearch = function () {
$scope.entrySearch = '';
};
$scope.viewSnapshot = function ($event,snapshot,embedId,entryId,$innerScope,domainObject) {
var viewAction = $scope.action.getActions({category: 'embed'})[0];
viewAction.perform($event, snapshot, embedId, entryId, $innerScope, domainObject);
};
$scope.parseText = function (text) {
if (text) {
return text.split(/\r\n|\r|\n/gi);
}
};
$scope.renderImage = function (img) {
return URL.createObjectURL(img);
};
$scope.getDomainObj = function (id) {
return objectService.getObjects([id]);
};
/*-----*/
function refreshComp(change) {
if (change && change.length) {
change[0].getCapability('action').getActions({key: 'remove'})[0].perform();
}
}
$scope.actionToMenuOption = function (action) {
return {
key: action.getMetadata().key,
name: action.getMetadata().name,
cssClass: action.getMetadata().cssClass,
perform: action.perform
};
};
// Maintain all "conclude-editing" and "save" actions in the
// present context.
function updateActions() {
$scope.menuEmbed = $scope.action ?
$scope.action.getActions({category: 'embed'}) :
[];
$scope.menuEmbedNoSnap = $scope.action ?
$scope.action.getActions({category: 'embed-no-snap'}) :
[];
$scope.menuActions = $scope.action ?
$scope.action.getActions({key: 'window'}) :
[];
}
// Update set of actions whenever the action capability
// changes or becomes available.
$scope.$watch("action", updateActions);
$scope.navigate = function ($event,embedType) {
if ($event) {
$event.preventDefault();
}
$scope.getDomainObj(embedType).then(function (resp) {
navigationService.setNavigation(resp[embedType]);
});
};
$scope.saveSnap = function (url,embedPos,entryPos) {
var snapshot = false;
if (url) {
if (embedPos !== -1 && entryPos !== -1) {
var reader = new window.FileReader();
reader.readAsDataURL(url);
reader.onloadend = function () {
snapshot = reader.result;
$scope.domainObject.useCapability('mutation', function (model) {
if (model.entries[entryPos]) {
model.entries[entryPos].embeds[embedPos].snapshot = {'src': snapshot,
'type': url.type,
'size': url.size,
'modified': Date.now()
};
model.entries[entryPos].embeds[embedPos].id = Date.now();
}
});
};
}
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryPos].embeds[embedPos].snapshot = snapshot;
});
}
};
/*---popups menu embeds----*/
function getEmbedActions(embedType) {
if (!$scope.embedActions.length) {
$scope.getDomainObj(embedType).then(function (resp) {
$scope.embedActions = [];
$scope.embedActions.push($scope.actionToMenuOption(
$scope.action.getActions({key: 'window',selectedObject: resp[embedType]})[0]
));
$scope.embedActions.push({
key: 'navigate',
name: 'Go to Original',
cssClass: '',
perform: function () {
$scope.navigate('', embedType);
}
});
});
}
}
$scope.openMenu = function ($event,embedType) {
$event.preventDefault();
getEmbedActions(embedType);
var body = $(document).find('body'),
initiatingEvent = agentService.isMobile() ?
'touchstart' : 'mousedown',
dismissExistingMenu,
menu,
popup;
var container = $($event.currentTarget).parent().parent();
menu = container.find('.menu-element');
// Remove the context menu
function dismiss() {
container.find('.hide-menu').append(menu);
body.off("mousedown", dismiss);
dismissExistingMenu = undefined;
$scope.embedActions = [];
}
// Dismiss any menu which was already showing
if (dismissExistingMenu) {
dismissExistingMenu();
}
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
popup = popupService.display(menu, [$event.pageX,$event.pageY], {
marginX: 0,
marginY: -50
});
// Stop propagation so that clicks or touches on the menu do not close the menu
menu.on(initiatingEvent, function (event) {
event.stopPropagation();
$timeout(dismiss, 300);
});
// Dismiss the menu when body is clicked/touched elsewhere
// ('mousedown' because 'click' breaks left-click context menus)
// ('touchstart' because 'touch' breaks context menus up)
body.on(initiatingEvent, dismiss);
};
$scope.$watchCollection("composition", refreshComp);
$scope.$on('$destroy', function () {});
}
return NotebookController;
});

View File

@@ -1,44 +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 SelectSnapshotController. */
define(
[],
function () {
function SelectSnapshotController($scope,$rootScope) {
$scope.selectModel = true;
function selectprint(value) {
$rootScope.selValue = value;
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = value;
}
$scope.$watch("selectModel", selectprint);
}
return SelectSnapshotController;
}
);

View File

@@ -1,140 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-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(['zepto'], function ($) {
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function EntryDnd($rootScope,$compile,dndService,typeService,notificationService) {
function link($scope, $element) {
function drop(e) {
var selectedObject = dndService.getData('mct-domain-object');
var selectedModel = selectedObject.getModel();
var cssClass = selectedObject.getCapability('type').typeDef.cssClass;
var entryId = -1;
var embedId = -1;
$scope.clearSearch();
if ($element[0].id === 'newEntry') {
entryId = $scope.domainObject.model.entries.length;
embedId = 0;
var lastEntry = $scope.domainObject.model.entries[entryId - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.push({'createdOn': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
});
});
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId - 1] =
{'createdOn': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
};
});
}
$scope.scrollToTop();
notificationService.info({
title: "Notebook Entry created"
});
}else {
entryId = $scope.domainObject.model.entries.map(function (x) {
return x.createdOn;
}).indexOf(+($element[0].id.replace('entry_', '')));
if (!$scope.domainObject.model.entries[entryId].embeds) {
$scope.domainObject.model.entries[entryId].embeds = [];
}
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId].embeds.push({'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
});
});
embedId = $scope.domainObject.model.entries[entryId].embeds.length - 1;
if (selectedObject) {
e.preventDefault();
}
}
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = selectedObject;
$scope.entryId = entryId;
$scope.embedId = embedId;
$compile(SNAPSHOT_TEMPLATE)($scope);
}
if ($(e.currentTarget).hasClass('drag-active')) {
$(e.currentTarget).removeClass('drag-active');
}
}
function dragover(e) {
if (!$(e.currentTarget).hasClass('drag-active')) {
$(e.currentTarget).addClass('drag-active');
}
}
// Listen for the drop itself
$element.on('dragover', dragover);
$element.on('drop', drop);
$scope.$on('$destroy', function () {
$element.off('dragover', dragover);
$element.off('drop', drop);
});
}
return {
restrict: 'A',
link: link
};
}
return EntryDnd;
});

View File

@@ -1,165 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-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([
'zepto'
], function (
$
) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
var NEW_NOTEBOOK_TEMPLATE = '<a class="s-button icon-notebook new-notebook-entry" title="New Notebook Entry">' +
'<span class="title-label">New Notebook Entry</span>' +
'</a>';
/**
* MCT Trigger Modal is intended for use in only one location: inside the
* object-header to allow views in a layout to be popped out in a modal.
* Users can close the modal and go back to normal, and everything generally
* just works fine.
*
* This code is sensitive to how our html is constructed-- particularly with
* how it locates the the container of an element in a layout. However, it
* should be able to handle slight relocations so long as it is always a
* descendent of a `.frame` element.
*/
function MCTModalNotebook($document) {
var document = $document[0];
function link($scope, $element) {
var frame = $element.parent();
for (var i = 0; i < 10; i++) {
if (frame.hasClass('frame')) {
break;
}
frame = frame.parent();
}
if (!frame.hasClass('frame')) {
$element.remove();
return;
}
frame = frame[0];
var layoutContainer = frame.parentElement,
isOpen = false,
toggleOverlay,
overlay,
closeButton,
doneButton,
notebookButton,
blocker,
overlayContainer,
notebookButtonEl;
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
document.body.appendChild(overlay);
layoutContainer.removeChild(frame);
overlayContainer.appendChild(frame);
//verify if there is a new notebook entry action
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
notebookButtonEl = document.createElement('div');
$(notebookButtonEl).addClass('notebook-button-container');
notebookButtonEl.innerHTML = NEW_NOTEBOOK_TEMPLATE;
notebookButton = frame.querySelector('.object-browse-bar .left');
notebookButton.appendChild(notebookButtonEl);
$(frame.querySelector('.object-holder')).addClass('container-notebook');
notebookButton.addEventListener('click', function () {
notebookAction[0].perform();
});
}
}
function closeOverlay() {
$(frame).addClass('frame frame-template');
overlayContainer.removeChild(frame);
layoutContainer.appendChild(frame);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
if (notebookButton) {
notebookButton.removeChild(notebookButtonEl);
}
}
toggleOverlay = function () {
if (!isOpen) {
openOverlay();
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
$element.on('click', toggleOverlay);
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
});
}
return {
restrict: 'A',
link: link
};
}
return MCTModalNotebook;
});

View File

@@ -1,134 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-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(['zepto'], function ($) {
/**
*/
function MCTSnapshot($rootScope,$document,exportImageService,dialogService,notificationService) {
var document = $document[0];
function link($scope, $element,$attrs) {
var element = $element[0];
var layoutContainer = element.parentElement,
toggleOverlay,
makeImg,
saveImg,
snapshot = document.createElement('div');
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(snapshot).addClass('abs overlay l-large-view snapshot');
snapshot.appendChild(element);
document.body.appendChild(snapshot);
}
function closeOverlay() {
if (snapshot) {
snapshot.removeChild(element);
layoutContainer.remove();
}
document.body.removeChild(snapshot);
snapshot = undefined;
$element.remove();
}
toggleOverlay = function () {
openOverlay();
makeImg(element);
};
makeImg = function (el) {
var scope = $scope;
var dialog = dialogService.showBlockingMessage({
title: "Saving...",
hint: "Taking Snapshot...",
unknownProgress: true,
severity: "info",
delay: true
});
this.$timeout(function () {
window.EXPORT_IMAGE_TIMEOUT = 5000;
exportImageService.exportPNGtoSRC(el).then(function (img) {
if (img) {
if (dialog) {
dialog.dismiss();
}
if ($element[0].dataset.entry && $element[0].dataset.embed) {
saveImg(img, +$element[0].dataset.entry, +$element[0].dataset.embed);
closeOverlay(false);
} else {
var reader = new window.FileReader();
reader.readAsDataURL(img);
reader.onloadend = function () {
//closeOverlay(true);
$($element[0]).attr("data-snapshot", reader.result);
$rootScope.snapshot = {'src': reader.result,
'type': img.type,
'size': img.size,
'modified': Date.now()
};
closeOverlay(false);
scope.$destroy();
};
}
} else {
dialog.dismiss();
}
}, function (error) {
if (dialog) {
dialog.dismiss();
}
closeOverlay();
});
}, 500);
window.EXPORT_IMAGE_TIMEOUT = 500;
};
saveImg = function (url,entryId,embedId) {
$scope.$parent.$parent.$parent.saveSnap(url, embedId, entryId);
};
if ($(document.body).find('.overlay.snapshot').length === 0) {
toggleOverlay();
}
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
$element.remove();
});
}
return {
restrict: 'A',
link: link
};
}
return MCTSnapshot;
});

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