mirror of
https://github.com/nasa/openmct.git
synced 2024-12-21 14:07:50 +00:00
Squash-merge open1193 into master.
Closes https://github.com/nasa/openmct/pull/1287. Squashed commit of the following: commit af9ba3095859684cb2465f1d5222a14db231fdb7 Merge:c98286d
31308b1
Author: Pete Richards <peter.l.richards@nasa.gov> Date: Fri Nov 25 14:57:32 2016 -0800 Merge branch 'open1193' into open1287-integration Resolve conflicts in glyph files as documented in pull request: https://github.com/nasa/openmct/pull/1287#issuecomment-263030180 Closes https://github.com/nasa/openmct/pull/1287 commit31308b1076
Author: Andrew Henry <andrew.k.henry@nasa.gov> Date: Tue Nov 22 17:08:10 2016 +0000 [Time Conductor] Addressed code review comments. Fixes #1287 commitdb6386e21c
Author: Henry <akhenry@gmail.com> Date: Wed Nov 9 11:52:39 2016 -0800 Removed redundant ConductorService commita9ec8db8c6
Merge:fc36674
dfa4591
Author: Henry <akhenry@gmail.com> Date: Wed Nov 9 08:23:07 2016 -0800 Merge branch 'master' into open1193 commitfc36674b5c
Author: Henry <akhenry@gmail.com> Date: Wed Nov 9 08:23:01 2016 -0800 Updated wording of docs commitd0906baccf
Author: Henry <akhenry@gmail.com> Date: Fri Nov 4 10:48:05 2016 -0700 Fixed TOI not showing commit099c56c407
Author: Henry <akhenry@gmail.com> Date: Fri Oct 28 16:46:06 2016 -0700 Fixed failing tests commit7cc008ed01
Author: Henry <akhenry@gmail.com> Date: Fri Oct 28 10:28:41 2016 -0700 Added tests for tables, TOI controller commitb0901e83cb
Author: Henry <akhenry@gmail.com> Date: Thu Oct 27 10:42:31 2016 -0700 Added tests for ConductorAxisController and TimeConductor commitdfed0a0783
Author: Henry <akhenry@gmail.com> Date: Wed Oct 26 18:07:50 2016 -0700 Added ConductorAxisController tests commitc3322e3847
Author: Henry <akhenry@gmail.com> Date: Tue Oct 25 20:54:24 2016 -0700 Added tests for MctTableController commit8e76ebb5a8
Author: Henry <akhenry@gmail.com> Date: Tue Oct 25 15:48:15 2016 -0700 Removed debugging code commit93735bc404
Author: Henry <akhenry@gmail.com> Date: Mon Oct 24 15:13:45 2016 -0700 Removed bundle definition of MctAxisController Documenting code commita942541724
Author: Henry <akhenry@gmail.com> Date: Sun Oct 23 19:54:01 2016 -0700 [Time Conductor] Fixed memory leak due to listeners not being deregistered commit49e600dcc9
Author: Henry <akhenry@gmail.com> Date: Sat Oct 22 16:47:11 2016 -0700 [Time Conductor] Fixed zoom slider behavior commitf5806613b9
Author: Henry <akhenry@gmail.com> Date: Sat Oct 22 14:46:14 2016 -0700 [Time Conductor] support TOI from real-time tables commit029d2b3058
Author: Henry <akhenry@gmail.com> Date: Thu Oct 20 14:32:34 2016 -0700 [Examples] Simplified MSL example, fixed object tree not loading by default, renamed. Fixes #1256. Fixes #1255 commit482eb4a5e8
Author: Henry <akhenry@gmail.com> Date: Sat Oct 22 13:09:05 2016 -0700 [Time Conductor] Using new API commit843c678b0b
Merge:b56ab0a
08ca765
Author: Henry <akhenry@gmail.com> Date: Fri Oct 21 10:21:22 2016 -0700 In process of merging commitb56ab0aac2
Author: Henry <akhenry@gmail.com> Date: Wed Oct 19 16:30:26 2016 -0700 [Time Conductor] Implement default sort, fix unpredictable positioning using % left, set TOI on conductor init. #1193 commitd12ae77d95
Author: Henry <akhenry@gmail.com> Date: Wed Oct 19 11:29:42 2016 -0700 Further TOI improvements commit22564473b5
Merge:9f3ec3b
86b51f6
Author: Henry <akhenry@gmail.com> Date: Tue Oct 18 16:32:12 2016 -0700 Merge branch 'open1193' of https://github.com/nasa/openmctweb into open1193 commit9f3ec3b18f
Author: Henry <akhenry@gmail.com> Date: Tue Oct 18 16:31:59 2016 -0700 Fixed issue with scrolling to row after bounds change commit86b51f6cde
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Oct 17 17:19:50 2016 -0700 [Frontend] Small refactor for mct-include Fixes #933 Fixes #1193 Markup and CSS tweaked to support using mct-include as the main container for the TOI element; TC, plots and tables all updated. commitdadca62955
Author: Henry <akhenry@gmail.com> Date: Mon Oct 17 15:54:34 2016 -0700 Positioning of TOI in tables and plots commit7a09bc1339
Author: Henry <akhenry@gmail.com> Date: Mon Oct 17 10:11:59 2016 -0700 Migrated TOI functionality to common controller commit6bea6b3bc2
Merge:660757f
8f67cbd
Author: Henry <akhenry@gmail.com> Date: Fri Oct 14 14:38:57 2016 -0700 Merge branch 'open1193' of https://github.com/nasa/openmctweb into open1193 commit660757fc1c
Author: Henry <akhenry@gmail.com> Date: Fri Oct 14 14:38:46 2016 -0700 Added TimeOfInterestController commit8f67cbd717
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Oct 14 11:33:35 2016 -0700 [Frontend] Fixed cursor: grab on Time Conductor Fixes #933 Fixes #1193 Moved cursor: grab into mixin; sass cleanups commit07a4e26317
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Oct 14 11:03:23 2016 -0700 [Frontend] TOI finalizing Fixes #933 Fixes #1193 HTML template cleaned up; checked in snow theme, theme colors tweaked; padding finalized; now uses val-to-left instead of -to-right; commit271c788f20
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Oct 14 10:22:26 2016 -0700 [Frontend] TOI in tables Fixes #933 Fixes #1193 WIP commitb7e8a1b378
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Oct 13 15:55:48 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: Table TOI element in progress commit6042e4ad58
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Oct 13 13:46:27 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: plots and TC done, moving on to tables; moved sass into conductor-v2; moved constants; commit1a534301c5
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Oct 13 11:42:10 2016 -0700 [Frontend] Refactor TOI element Fixes #933 Fixes #1193 WIP; TOI as mct-include; layout uses flex-box; preparing to move TOI sass into conductor-v2 directory; commit42acf9255e
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Oct 12 18:34:51 2016 -0700 [Frontend] Adding resync and dedicated unpin buttons Fixes #933 Fixes #1193 VERY WIP! commit3f0eb0b7cb
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Oct 12 18:33:53 2016 -0700 [Frontend] New glyphs for use by TOI Fixes #933 Fixes #1193 Symbols font updated commit086307ba3a
Author: Henry <akhenry@gmail.com> Date: Tue Oct 11 17:16:51 2016 -0700 Fixed scrolling behavior with TOI commit938bf3c4df
Merge:3910437
8b2047c
Author: Henry <akhenry@gmail.com> Date: Tue Oct 11 13:26:20 2016 -0700 Merge branch 'open1182' into open1193 commit8b2047ca32
Author: Henry <akhenry@gmail.com> Date: Tue Oct 11 13:25:24 2016 -0700 Fixed issue with setting deltas commit3910437033
Merge:70c4ce2
02c543f
Author: Henry <akhenry@gmail.com> Date: Tue Oct 11 12:48:37 2016 -0700 Merged changes to zoom commit02c543fddc
Author: Henry <akhenry@gmail.com> Date: Tue Oct 11 12:45:18 2016 -0700 Fixed zoom in real-time mode commit70c4ce24e4
Author: Henry <akhenry@gmail.com> Date: Fri Oct 7 17:57:14 2016 -0700 Added support for clicking row to set TOI commitb995a8b87b
Merge:51a9557
b384e84
Author: Henry <akhenry@gmail.com> Date: Fri Oct 7 16:11:59 2016 -0700 Merged from open1182 commitb384e84872
Merge:5babf72
07140b1
Author: Henry <akhenry@gmail.com> Date: Fri Oct 7 15:02:41 2016 -0700 Merge branch 'open933' into open1182 commit07140b179f
Merge:3e9c0eb
cbd001e
Author: Henry <akhenry@gmail.com> Date: Fri Oct 7 14:54:18 2016 -0700 Merge branch 'master' into open933 commit51a95575f7
Author: Henry <akhenry@gmail.com> Date: Fri Oct 7 14:51:59 2016 -0700 [Time Conductor] Refactored time of interest as optional generic behavior of MctTable commitdfbbc3b0c5
Merge:430645b
47a0aba
Author: Henry <akhenry@gmail.com> Date: Thu Oct 6 10:23:42 2016 -0700 alt-click to select TOI from table commit3e9c0eb7a5
Merge:f1d2072
8a00181
Author: Henry <akhenry@gmail.com> Date: Wed Oct 5 12:25:04 2016 -0700 Merged from master to resolve build issues commitf1d2072bb9
Author: Henry <akhenry@gmail.com> Date: Wed Oct 5 11:17:22 2016 -0700 Added license information commit430645b1f2
Author: Henry <akhenry@gmail.com> Date: Wed Oct 5 09:04:13 2016 -0700 TOI working in time conductor commit47a0aba601
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Tue Oct 4 18:12:01 2016 -0700 [Frontend] TOI sass and markup sanding Fixes #933 Fixes #1193 Color tweaks; Cleanups, commented code removal, etc.; tightened up tabular padding and font sizes; commit0ed0a48a8c
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Tue Oct 4 15:49:18 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 Tabular styling for TOI; commit1650aae518
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Oct 3 15:56:56 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: Tabular styling for TOI; TODO: make bottom border work commitd3bf6c5857
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Oct 3 15:15:56 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: Markup and CSS for plots and TC near complete; TODO: revised styling in tabular views; commit5cd0c8a4fa
Author: Henry <akhenry@gmail.com> Date: Fri Sep 30 12:41:14 2016 -0700 [Time Conductor] merged from open1182 commit5babf7274d
Author: Henry <akhenry@gmail.com> Date: Thu Sep 29 11:21:11 2016 -0700 [Time Conductor] Tweaked the break points for zoom level indicator commit22da34870d
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 29 10:41:21 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: Markup and CSS revisions for updated UX approach; TODO: cosmetic CSS commit99253a5904
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Tue Sep 27 20:42:33 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: Markup and CSS revisions for updated UX approach commit2db4aa6235
Author: Henry <akhenry@gmail.com> Date: Fri Sep 23 13:06:22 2016 -0700 [Time Conductor] Added zoom level label commitbb2ae2f8d1
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Fri Sep 23 11:07:06 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 Finalized TOI in plots, table and TC commit0cf4c92555
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 22 19:15:10 2016 -0700 [Frontend] Styling for TOI element in tables Fixes #933 Fixes #1193 WIP: styling in tabular mostly done, needs more unit testing TODO: fix hide/show for pinned TOI in plots - don't show pinned TOI on hover when not .active commit3c95c095f1
Author: Henry <akhenry@gmail.com> Date: Thu Sep 22 17:22:25 2016 -0700 [Time Conductor] Refactored out use of angular event bus in favor of making TimeConductorViewService an event emitter. commitadbcc407ef
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 22 16:06:45 2016 -0700 [Frontend] Styling for TOI element Fixes #933 Fixes #1193 WIP: TOI in tables commit1c3bd69b66
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 22 16:06:16 2016 -0700 [Frontend] Styling for TOI element, some refactoring Fixes #933 Fixes #1193 Moves some TOI styling into dedicated new scss file; Enhancements to TOI in plots to bring into parity with TOI in TC approach; commit49ee5cb74b
Author: Henry <akhenry@gmail.com> Date: Thu Sep 22 15:18:48 2016 -0700 [Time Conductor] Destroy listeners in ConductorAxisController commit1a93ba2c3d
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 22 15:09:42 2016 -0700 [Frontend] Styling for TOI element in TC Fixes #933 Fixes #1193 Final for now; both types of TOI added to TC markup; Hover behavior; constant values in the right places; commit98122cc730
Author: Henry <akhenry@gmail.com> Date: Tue Sep 20 11:40:58 2016 -0700 [Time Conductor] Added Zoom commit7fcafb6b58
Author: Henry <akhenry@gmail.com> Date: Mon Sep 12 16:53:48 2016 -0700 [Time Conductor] Added pan to Time Conductor commitd77922d66c
Author: Pete Richards <pete@pete-richards.com> Date: Thu Sep 15 09:37:11 2016 -0700 Revert "[proxyUrl] pass URL parameters to proxied URL" commit2e81550c86
Author: Victor Woeltjen <victor.woeltjen@nasa.gov> Date: Wed Sep 14 10:16:33 2016 -0700 Revert "[Build] Check dependencies for vulnerabilities" commit8eb7585653
Author: Victor Woeltjen <victor.woeltjen@nasa.gov> Date: Mon Aug 29 10:58:02 2016 -0700 [README] Warn about root installation issues Mitigates #1151. commit904d56a089
Author: Henry <akhenry@gmail.com> Date: Thu Sep 22 13:32:43 2016 -0700 [Time Conductor] #933 Clean up time conductor listeners on scope destruction commitc0a96b3c5f
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 22 10:33:05 2016 -0700 [Frontend] Styling for TOI element in TC Fixes #933 Fixes #1193 WIP, tweaking TOI in TC commit27e6caf69b
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Sep 16 18:51:51 2016 -0700 [Frontend] Styling for TOI element in plots Fixes #933 Fixes #1193 Relates to #1182 WIP, adding TOI to plot view; Integrated Andrew's work from #1182; Significant changes to markup and additions to plot scss; commit2c7ae95106
Merge:2c81b72
41a160f
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Sep 16 15:11:25 2016 -0700 Merge branch 'open1182' into open1193 commit41a160fc4f
Merge:92a80c3
156ba83
Author: Henry <akhenry@gmail.com> Date: Fri Sep 16 14:25:49 2016 -0700 Merge branch 'master' into open1182 commit92a80c39cb
Author: Henry <akhenry@gmail.com> Date: Mon Sep 12 16:53:48 2016 -0700 [Time Conductor] Added pan to Time Conductor commit2c81b72d60
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 15 16:02:30 2016 -0700 [Frontend] Styling for TOI element in plots Fixes #933 Fixes #1193 WIP, adding TOI to plot view. commit9e2debf801
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 15 15:43:29 2016 -0700 [Frontend] Styling for TC's TOI element Fixes #933 Fixes #1193 I lied in my last commit - one more alignment tweak needed. Now I'm done. commit35872e284c
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 15 15:40:03 2016 -0700 [Frontend] Styling for TC's TOI element Fixes #933 Fixes #1193 TOI in TC done for now. commit98e67f8dfb
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Thu Sep 15 14:55:06 2016 -0700 [Frontend] Styling for TC's TOI element Fixes #933 Fixes #1193 WIP: adjust spacing; added pagination buttons commitf912b9e273
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Sep 14 18:40:55 2016 -0700 [Frontend] Styling for TC's TOI element Fixes #933 WIP, TC v2 changes commit2df1e2b508
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Sep 14 18:40:29 2016 -0700 [Frontend] Styling for TC's TOI element Fixes #933 WIP, global changes commit9e85341aaa
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Sep 14 15:16:16 2016 -0700 [Frontend] Fixed color for TC clock hands Fixes #933 commit11e06039ec
Merge:a1331b7
f732387
Author: Henry <akhenry@gmail.com> Date: Tue Sep 13 13:25:37 2016 -0700 Merge branch 'master' into open933 commita1331b7bb3
Merge:d1960b2
e73bb4f
Author: Henry <akhenry@gmail.com> Date: Mon Sep 12 15:07:05 2016 -0700 Merge branch 'master' into open933 commitd1960b2f46
Author: Henry <akhenry@gmail.com> Date: Mon Sep 12 14:54:16 2016 -0700 [Time Conductor] Resolved merge conflicts commit9a06325533
Merge:e639e05
4c6ca58
Author: Henry <akhenry@gmail.com> Date: Mon Sep 12 14:39:24 2016 -0700 Merge branch 'master' into open933 commite639e056ba
Author: Henry <akhenry@gmail.com> Date: Fri Sep 9 16:39:21 2016 -0700 [Time Conductor] Fixing bugs found in smoke testing. Fixes #933 commitfbab890081
Author: Henry <akhenry@gmail.com> Date: Wed Sep 7 14:29:21 2016 -0700 [Time Conductor] Switched conductor to mct-include rather than mct-representation to avoid warnings commitd37dd52ee1
Merge:7af5875
58391de
Author: Henry <akhenry@gmail.com> Date: Tue Sep 6 16:43:17 2016 -0700 Merge branch 'master' into open933 commit7af5875dd5
Author: Henry <akhenry@gmail.com> Date: Tue Sep 6 10:04:29 2016 -0700 [Time Conductor] #933 Fixed code style errors commitc6eaa3d528
Author: Andrew Henry <andrew.k.henry@nasa.gov> Date: Wed Aug 24 15:28:19 2016 +0100 [Time Conductor] Adding tests and fixing failing ones. #933 commit4cf6126d35
Author: Henry <akhenry@gmail.com> Date: Wed Aug 10 18:22:30 2016 -0700 Refactoring based on feedback Refactoring controller Migrating functions off controller onto service class Simplified modes Adding comments Removed unnecessary validation Fixing testing issues commit4ae6da0334
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Tue Aug 9 17:47:31 2016 -0700 [Frontend] Data viz in Time Conductor smaller Fixes #933 Reduced height of data viz bar in Time Conductor v2; commitae39343b76
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Aug 8 12:14:20 2016 -0700 [Frontend] Fix for bad fix Fixes #1112 Put overflow: hidden back at outer wrapper level (now on .t-object.primary-pane ) which doens't clip the Inspector expand/collapse; did better unit testing; commit62ee7e569b
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Aug 8 11:21:01 2016 -0700 [Frontend] Fix for collapse Inspector button Fixes #1112 Moved min-width and overflow: hidden to TC-specific elements; removed overflow: hidden from .primary-pane commit7557a86208
Merge:46e644e
c8931f8
Author: Henry <akhenry@gmail.com> Date: Fri Aug 5 16:37:36 2016 -0700 Merge branch 'master' into open933 commit46e644e6dc
Author: Pete Richards <peter.l.richards@nasa.gov> Date: Fri Aug 5 14:44:18 2016 -0700 Use key to retrieve default commitaf7954c5a1
Author: Pete Richards <peter.l.richards@nasa.gov> Date: Fri Aug 5 11:24:51 2016 -0700 Trigger digests when bounds are set commit0e0ad64830
Author: Henry <akhenry@gmail.com> Date: Thu Aug 4 10:41:58 2016 -0700 Fixed issue with wrong deltas being applied commitf3fd386e3b
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 21:03:09 2016 -0700 Retain time system on mode change commit25b9f371e2
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 20:16:03 2016 -0700 Fixed loss of time system options on navigation commit6b482d487b
Merge:f96f78f
9a72c96
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 19:57:03 2016 -0700 Merged mode-specific defaults, with some refactoring commitf96f78ff79
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 19:34:31 2016 -0700 Select appropriate tick source based on mode commit579233ade9
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 18:30:01 2016 -0700 Fixed delta format issue on navigation commit9a72c96ea4
Author: Pete Richards <peter.l.richards@nasa.gov> Date: Wed Aug 3 18:06:39 2016 -0700 [TCv2] different defaults by mode commitf4e1879a2d
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 17:43:07 2016 -0700 stop listening to tick source on time system change commitf844495cc1
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 17:40:37 2016 -0700 Support deltaFormat on timeSystems commit900752208f
Author: Pete Richards <peter.l.richards@nasa.gov> Date: Wed Aug 3 13:05:43 2016 -0700 [TCv2] get conductor without service commit6501e2eb5f
Author: Henry <akhenry@gmail.com> Date: Wed Aug 3 12:22:13 2016 -0700 Added isUTCBased to TimeSystem interface commitb9c41107c1
Author: Henry <akhenry@gmail.com> Date: Tue Aug 2 22:18:44 2016 -0700 Time Conductor state retained on navigation commit34c62ba405
Author: Henry <akhenry@gmail.com> Date: Tue Aug 2 15:16:35 2016 -0700 Time Conductor in edit mode commit1eea5ce480
Merge:4cd579d
1173828
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 20:29:50 2016 -0700 merged from master commit4cd579d274
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 20:16:46 2016 -0700 Pass numerical value to format functions commit11738286df
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 18:56:33 2016 -0700 [Frontend] Styling for unsynced elements Fixes #933 commitca5206d4a0
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 18:55:49 2016 -0700 [Frontend] Fixing issues with theme coloring Fixes #933 commit573f1f9f99
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 18:04:17 2016 -0700 [Frontend] Hide zoom slider control Fixes #933 Temporarily hiding per request from Andrew today; commitc5c45f0a0e
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 18:01:28 2016 -0700 [Frontend] Update TC2 markup and sass Fixes #933 Update markup and sass in TC2 to be in line with updates from master from #1047 glyphs to cssclass approach; commit121ab413ff
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 17:51:15 2016 -0700 Apply formatting, filter modes by tick source availability commit753bd97c8a
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 17:12:44 2016 -0700 Merge remote-tracking branch 'origin/master' into open933-c # Conflicts: # platform/commonUI/edit/res/templates/create/create-menu.html # platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot # platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg # platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf # platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff # platform/commonUI/general/res/sass/_archetypes.scss # platform/commonUI/general/res/sass/_constants.scss # platform/commonUI/general/res/sass/_icons.scss # platform/commonUI/general/res/sass/_main.scss # platform/commonUI/general/res/sass/_mixins.scss # platform/commonUI/general/res/sass/controls/_buttons.scss # platform/commonUI/general/res/templates/controls/time-controller.html # platform/commonUI/themes/snow/res/sass/_constants.scss commitfcd7ab93e5
Merge:a75ea67
9b58aa0
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 17:12:00 2016 -0700 Merge branch 'open933' of https://github.com/nasa/openmctweb into open933 commitc699cb8b4b
Merge:579c6b6
d1e1ba1
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 17:11:37 2016 -0700 Merge remote-tracking branch 'origin/master' into open933-c # Conflicts: # platform/commonUI/edit/res/templates/create/create-menu.html # platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot # platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg # platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf # platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff # platform/commonUI/general/res/sass/_archetypes.scss # platform/commonUI/general/res/sass/_constants.scss # platform/commonUI/general/res/sass/_icons.scss # platform/commonUI/general/res/sass/_main.scss # platform/commonUI/general/res/sass/_mixins.scss # platform/commonUI/general/res/sass/controls/_buttons.scss # platform/commonUI/general/res/templates/controls/time-controller.html # platform/commonUI/themes/snow/res/sass/_constants.scss commita75ea67b8c
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 17:11:01 2016 -0700 Format updates when time system selected commit9b58aa0052
Author: Pete Richards <peter.l.richards@nasa.gov> Date: Mon Aug 1 17:03:00 2016 -0700 [TCv2] Add conductorService for compatibility Add conductor service for compatibility with old plugins that depend on the conductor service. commit579c6b6d24
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 16:30:51 2016 -0700 [Frontend] Styling TC unsynced elements Fixes #933 WIP: Styling for unsynced elements commit762f43fa61
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 16:26:47 2016 -0700 [Frontend] Styling TC unsynced elements Fixes #933 WIP: Styling for unsynced elements commitce5d0ef5bd
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 16:03:27 2016 -0700 Merged stylesheet changes commit142ee2f336
Merge:482fcbf
523d674
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 15:44:49 2016 -0700 Added LocalTimeSystem and merged latest styles commit482fcbf6ee
Author: Henry <akhenry@gmail.com> Date: Mon Aug 1 15:14:23 2016 -0700 Refactored bundle commit523d6743fb
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Mon Aug 1 12:00:53 2016 -0700 [Frontend] Added support for thematic styling of Time Conductor v2 Fixes #933 Added theme sass files commit7af22126d4
Author: Henry <akhenry@gmail.com> Date: Wed Jul 27 12:05:03 2016 -0400 Prevent tabbing into end bounds when not in fixed mode commit8e59072537
Author: Henry <akhenry@gmail.com> Date: Wed Jul 27 11:55:12 2016 -0400 Removed LAD and Realtime modes commitc1bbc4f01d
Author: Henry <akhenry@gmail.com> Date: Wed Jul 27 10:38:04 2016 -0400 Code cleanup commitaa4a5e56f9
Author: Henry <akhenry@gmail.com> Date: Tue Jul 26 16:55:00 2016 -0400 Improved support from plot commit5b2eb72b16
Author: Henry <akhenry@gmail.com> Date: Tue Jul 26 08:33:30 2016 -0400 [Time Conductor] Addressed documentation issues commit1b7fc57d21
Author: Henry <akhenry@gmail.com> Date: Mon Jul 25 16:55:27 2016 -0400 Added status support to plots commita4f6f6f50b
Author: Henry <akhenry@gmail.com> Date: Mon Jul 25 12:09:15 2016 -0400 Added license commit19fd63b850
Merge:c2c8e16
a10ded2
Author: Henry <akhenry@gmail.com> Date: Thu Jul 21 20:06:50 2016 -0700 Merge branch 'open933-frontend-b' into open933 commitc2c8e16453
Author: Henry <akhenry@gmail.com> Date: Thu Jul 21 20:03:40 2016 -0700 Added scale sensitive formatting to UTCTimeFormat commita10ded25b4
Merge:da7c636
e6d8944
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 20 18:33:58 2016 -0700 Merge remote-tracking branch 'origin/open933' into open933-frontend-b # Conflicts: # platform/features/conductor-v2/res/sass/time-conductor.scss # platform/features/conductor/res/sass/time-conductor.scss commitda7c636724
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 20 18:22:20 2016 -0700 [Frontend] Time Conductor v2 styling Fixes #933 Redo TC icon to use font symbol, added new symbol for brackets to font files; font anti-aliasing mod for .ui-symbol class; layout tweaks; mobile tweaks. commitb392633bc6
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 20 15:48:22 2016 -0700 [Frontend] Time Conductor v2 styling Fixes #933 WIP: Significant mobile and desktop style tweaks; moved constants into their own include file; commitff1678435e
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 20 11:43:40 2016 -0700 [Frontend] Time Conductor v2 styling Fixes #933 Changed desktop and mobile RT UI to display end datetime and hide start; WIP: mobile styling for main UI of TC; commit2124fe01e1
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 20 10:18:42 2016 -0700 [Frontend] Renew support for Time Conductor v1 Fixes #933 Minor fixes to TCv1 for mobile commite6d8944547
Author: Henry <akhenry@gmail.com> Date: Tue Jul 19 20:17:06 2016 -0700 Modified main.js commitea1defac28
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Tue Jul 19 18:33:24 2016 -0700 [Frontend] Renew support for Time Conductor v1 Fixes #933 Time Conductors v1 and v2 now build and load their own isolated CSS files. All previous styling for TCv1 should be re-enabled. Note that Conductor v2 mobile is not complete yet. commit2a19394334
Author: Henry <akhenry@gmail.com> Date: Tue Jul 19 19:54:52 2016 -0700 Added compatibility layer to support existing plots and historical tables commitf641edbce7
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Tue Jul 19 18:33:24 2016 -0700 [Frontend] Renew support for Time Conductor v1 Fixes #933 Time Conductors v1 and v2 now build and load their own isolated CSS files. All previous styling for TCv1 should be re-enabled. Note that Conductor v2 mobile is not complete yet. commit15a608a861
Author: Henry <akhenry@gmail.com> Date: Mon Jul 18 18:44:29 2016 -0700 Populate format in input fields commit334ca64551
Merge:0af49ef
4087b9c
Author: Henry <akhenry@gmail.com> Date: Mon Jul 18 14:25:02 2016 -0700 Merged open933-frontend commit0af49efe06
Author: Henry <akhenry@gmail.com> Date: Mon Jul 18 08:23:27 2016 -0700 Refactored out modes, time systems, etc. commit4087b9cdde
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Fri Jul 15 14:39:29 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 WIP: Fixed look for Firefox commit43a804eef4
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Jul 15 07:54:32 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 WIP: Added zoom current range indicator; tweaks to style commitb3a4f52fe2
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Thu Jul 14 18:30:49 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 WIP: Adding zoom control with HTML5 input range type; Refactored sass slightly to move display: inline-block out of mixin containerBase and into .s-btn. commit671e3016d4
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Thu Jul 14 16:40:05 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 New _animations scss include, moved scss around. commit379828315f
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Thu Jul 14 16:39:27 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 New _animations scss include, moved scss around. commit8c5538ec4d
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Thu Jul 14 14:58:18 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 "Sticky" clock anim for LAD mode commit2f9fbfef7f
Author: Henry <akhenry@gmail.com> Date: Wed Jul 13 20:33:47 2016 -0700 More refactoring commit2baca659ca
Author: Henry <akhenry@gmail.com> Date: Wed Jul 13 19:50:58 2016 -0700 Refactoring commit8b694ef337
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 19:42:51 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 In-progress: color/size tweaks, fixes for espresso theme commite193e3dfba
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 19:16:27 2016 -0700 Merge open933 latest, resolve conflicts Fixes #933 Fair amount of manual fixing in time-conductor.html commit8214c8e895
Merge:33b2225
14463d3
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 19:16:00 2016 -0700 Merge open933 latest, resolve conflicts Fixes #933 Fair amount of manual fixing in time-conductor.html commit33b2225d10
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 18:48:17 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 In-progress: restructured markup to move modeModel farther out; animated icon commit14463d39a8
Author: Henry <akhenry@gmail.com> Date: Wed Jul 13 18:17:27 2016 -0700 Added end delta commitfcfda50e73
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 15:00:16 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 In-progress: color tweaks, bar sizing, field widths commit06af84c161
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 13:14:32 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 In-progress: fixed SVG text color, field styling for fixed vs. real-time, markup cleanup commit5238aa2731
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Wed Jul 13 08:07:59 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 In-progress; fixed SVG text color commitfd29473664
Author: Henry <akhenry@gmail.com> Date: Tue Jul 12 15:02:39 2016 -0700 Support resize commit97f3fd516b
Author: Henry <akhenry@gmail.com> Date: Tue Jul 12 10:14:26 2016 -0700 Changed default duration to fifteen minutes commit088416905d
Author: Henry <akhenry@gmail.com> Date: Tue Jul 12 10:08:08 2016 -0700 Added duration commit2056d87453
Merge:585da38
64ce8a2
Author: Henry <akhenry@gmail.com> Date: Mon Jul 11 14:27:30 2016 -0700 Merge branch 'open933-frontend' into open933 commit64ce8a2b2a
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Jul 11 14:03:41 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 Color adjusts, mini super-menu size and font tweaks, glyphs added to selector, SVG style fixes in progress commit585da38a16
Author: Henry <akhenry@gmail.com> Date: Mon Jul 11 13:46:46 2016 -0700 Fixed some merge issues commitbf0e85a94c
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Jul 11 11:35:26 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #933 Renamed main class; removed unused <style> defs commit84b7a9dc2f
Merge:7b7b21d
11caa83
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Jul 11 11:29:35 2016 -0700 Merge remote-tracking branch 'origin/open933' into open933-frontend Fixes #933 Conflicts: platform/features/conductor-v2/src/TimeConductorController.js commit11caa8396a
Author: Henry <akhenry@gmail.com> Date: Mon Jul 11 11:18:23 2016 -0700 Updated modes commit0017b77439
Merge:5cc81ba
788483e
Author: Henry <akhenry@gmail.com> Date: Mon Jul 11 11:06:22 2016 -0700 Merged markup changes commit7b7b21d748
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Jul 11 11:05:47 2016 -0700 [Frontend] Styling of Time Conductor v2 Fixes #933 Tweaks to language; tweak to class name in markup commit788483ec13
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Jul 11 10:37:08 2016 -0700 [Frontend] Styling of Time Conductor v2 Fixes #933 Tweaks to language commit7b19f91ce6
Merge:0a0bc55
4e7b69c
Author: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Date: Mon Jul 11 10:31:14 2016 -0700 [Frontend] Merge latest from open933 Fixes #933 Resolve conflicts in mode-menu.html, mode-selector.html, time-conductor.html; apply tweaks, language, etc. commit5cc81ba12a
Author: Henry <akhenry@gmail.com> Date: Mon Jul 11 10:26:34 2016 -0700 [Time Conductor] Added mode class to time conductor commit0a0bc55f5f
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Jul 8 17:11:43 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #993 In-progress; mode menu names and descriptors modified, markup cleaned up commit4e7b69c4df
Author: Henry <akhenry@gmail.com> Date: Fri Jul 8 16:57:50 2016 -0700 Enabled fixed and real-time modes commitcf83040c4b
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Jul 8 16:54:49 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #993 In-progress; Create menu refactoring and new mini Create menu commit32f7bc86af
Author: Charles Hacskaylo <charlesh88@gmail.com> Date: Fri Jul 8 16:54:13 2016 -0700 [Frontend] Styling for Time Conductor v2 Fixes #993 In-progress; class renaming continued, cleanups in markup file, in-page styles ported to scss commite230b92946
Author: Henry <akhenry@gmail.com> Date: Fri Jul 8 15:15:12 2016 -0700 Fixed bug with date selector having to be clicked twice commit58ed500ecf
Author: Henry <akhenry@gmail.com> Date: Thu Jul 7 16:57:03 2016 -0700 Time sync via conductor commitbca5eb0fdb
Author: Henry <akhenry@gmail.com> Date: Thu Jul 7 09:47:46 2016 -0700 [Time Conductor] #933 Initial markup
This commit is contained in:
parent
c98286d426
commit
79b4f9a0f4
@ -99,7 +99,7 @@ define([
|
||||
"source": "generator",
|
||||
"domains": [
|
||||
{
|
||||
"key": "time",
|
||||
"key": "utc",
|
||||
"name": "Time"
|
||||
},
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ define([
|
||||
"domains": [
|
||||
{
|
||||
"name": "Time",
|
||||
"key": "timestamp",
|
||||
"key": "utc",
|
||||
"format": "utc"
|
||||
}
|
||||
]
|
||||
|
@ -95,6 +95,45 @@ define([
|
||||
})[0][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the current range of the time conductor's
|
||||
* bounds.
|
||||
* @param timeRange
|
||||
* @returns {*}
|
||||
*/
|
||||
UTCTimeFormat.prototype.timeUnits = function (timeRange) {
|
||||
var momentified = moment.duration(timeRange);
|
||||
|
||||
return [
|
||||
["Decades", function (r) {
|
||||
return r.years() > 15;
|
||||
}],
|
||||
["Years", function (r) {
|
||||
return r.years() > 1;
|
||||
}],
|
||||
["Months", function (r) {
|
||||
return r.years() === 1 || r.months() > 1;
|
||||
}],
|
||||
["Days", function (r) {
|
||||
return r.months() === 1 || r.days() > 1;
|
||||
}],
|
||||
["Hours", function (r) {
|
||||
return r.days() === 1 || r.hours() > 1;
|
||||
}],
|
||||
["Minutes", function (r) {
|
||||
return r.hours() === 1 || r.minutes() > 1;
|
||||
}],
|
||||
["Seconds", function (r) {
|
||||
return r.minutes() === 1 || r.seconds() > 1;
|
||||
}],
|
||||
["Milliseconds", function (r) {
|
||||
return true;
|
||||
}]
|
||||
].filter(function (row) {
|
||||
return row[1](momentified);
|
||||
})[0][0];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
|
@ -58,5 +58,26 @@ define([
|
||||
expect(format.format(APRIL, scale)).toBe("April");
|
||||
expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016");
|
||||
});
|
||||
|
||||
it("Returns appropriate time units for a given time span", function () {
|
||||
var ONE_DAY = 1000 * 60 * 60 * 24;
|
||||
var FIVE_DAYS = 5 * ONE_DAY;
|
||||
var FIVE_MONTHS = 60 * ONE_DAY;
|
||||
|
||||
var ONE_YEAR = 365 * ONE_DAY;
|
||||
var SEVEN_YEARS = 7 * ONE_YEAR;
|
||||
var TWO_DECADES = 20 * ONE_YEAR;
|
||||
|
||||
//A span of one day should show a zoom label of "Hours"
|
||||
expect(format.timeUnits(ONE_DAY)).toEqual("Hours");
|
||||
//Multiple days should display "Days"
|
||||
expect(format.timeUnits(FIVE_DAYS)).toEqual("Days");
|
||||
expect(format.timeUnits(FIVE_MONTHS)).toEqual("Days");
|
||||
//A span of one year should show a zoom level of "Months".
|
||||
// Multiple years will show "Years"
|
||||
expect(format.timeUnits(ONE_YEAR)).toEqual("Months");
|
||||
expect(format.timeUnits(SEVEN_YEARS)).toEqual("Years");
|
||||
expect(format.timeUnits(TWO_DECADES)).toEqual("Decades");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "openmct-symbols-16px",
|
||||
"lastOpened": 1479173088107,
|
||||
"created": 1479173085258
|
||||
"lastOpened": 1480112601593,
|
||||
"created": 1480112580248
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@ -564,13 +564,21 @@
|
||||
"code": 921664,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 120,
|
||||
"id": 105,
|
||||
"name": "icon-resync",
|
||||
"prevSize": 24,
|
||||
"code": 921655,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 37,
|
||||
"prevSize": 24,
|
||||
"name": "icon-activity",
|
||||
"id": 32,
|
||||
"code": 921856,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 36,
|
||||
@ -578,7 +586,7 @@
|
||||
"name": "icon-activity-mode",
|
||||
"id": 31,
|
||||
"code": 921857,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 52,
|
||||
@ -586,7 +594,7 @@
|
||||
"name": "icon-autoflow-tabular",
|
||||
"id": 47,
|
||||
"code": 921858,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 55,
|
||||
@ -594,7 +602,7 @@
|
||||
"name": "icon-clock",
|
||||
"id": 50,
|
||||
"code": 921859,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 58,
|
||||
@ -602,7 +610,7 @@
|
||||
"name": "icon-database",
|
||||
"id": 53,
|
||||
"code": 921860,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 57,
|
||||
@ -610,7 +618,7 @@
|
||||
"name": "icon-database-query",
|
||||
"id": 52,
|
||||
"code": 921861,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 17,
|
||||
@ -618,7 +626,7 @@
|
||||
"name": "icon-dataset",
|
||||
"id": 12,
|
||||
"code": 921862,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 22,
|
||||
@ -626,7 +634,7 @@
|
||||
"name": "icon-datatable",
|
||||
"id": 17,
|
||||
"code": 921863,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 59,
|
||||
@ -634,7 +642,7 @@
|
||||
"name": "icon-dictionary",
|
||||
"id": 54,
|
||||
"code": 921864,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 62,
|
||||
@ -642,7 +650,7 @@
|
||||
"name": "icon-folder",
|
||||
"id": 57,
|
||||
"code": 921865,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 66,
|
||||
@ -650,7 +658,7 @@
|
||||
"name": "icon-image",
|
||||
"id": 61,
|
||||
"code": 921872,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 68,
|
||||
@ -658,7 +666,7 @@
|
||||
"name": "icon-layout",
|
||||
"id": 63,
|
||||
"code": 921873,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 77,
|
||||
@ -666,7 +674,7 @@
|
||||
"name": "icon-object",
|
||||
"id": 72,
|
||||
"code": 921874,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 78,
|
||||
@ -674,7 +682,7 @@
|
||||
"name": "icon-object-unknown",
|
||||
"id": 73,
|
||||
"code": 921875,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 79,
|
||||
@ -682,7 +690,7 @@
|
||||
"name": "icon-packet",
|
||||
"id": 74,
|
||||
"code": 921876,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 80,
|
||||
@ -690,7 +698,7 @@
|
||||
"name": "icon-page",
|
||||
"id": 75,
|
||||
"code": 921877,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 114,
|
||||
@ -698,7 +706,7 @@
|
||||
"name": "icon-plot-overlay",
|
||||
"prevSize": 24,
|
||||
"code": 921878,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 113,
|
||||
@ -706,7 +714,7 @@
|
||||
"name": "icon-plot-stacked",
|
||||
"prevSize": 24,
|
||||
"code": 921879,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 10,
|
||||
@ -714,7 +722,7 @@
|
||||
"name": "icon-session",
|
||||
"id": 5,
|
||||
"code": 921880,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 24,
|
||||
@ -722,7 +730,7 @@
|
||||
"name": "icon-tabular",
|
||||
"id": 19,
|
||||
"code": 921881,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 7,
|
||||
@ -730,7 +738,7 @@
|
||||
"name": "icon-tabular-lad",
|
||||
"id": 2,
|
||||
"code": 921888,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 6,
|
||||
@ -738,7 +746,7 @@
|
||||
"name": "icon-tabular-lad-set",
|
||||
"id": 1,
|
||||
"code": 921889,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 8,
|
||||
@ -746,7 +754,7 @@
|
||||
"name": "icon-tabular-realtime",
|
||||
"id": 3,
|
||||
"code": 921890,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 23,
|
||||
@ -754,7 +762,7 @@
|
||||
"name": "icon-tabular-scrolling",
|
||||
"id": 18,
|
||||
"code": 921891,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 112,
|
||||
@ -762,7 +770,7 @@
|
||||
"name": "icon-telemetry",
|
||||
"id": 86,
|
||||
"code": 921892,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 90,
|
||||
@ -770,7 +778,7 @@
|
||||
"name": "icon-telemetry-panel",
|
||||
"id": 85,
|
||||
"code": 921893,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 93,
|
||||
@ -778,7 +786,7 @@
|
||||
"name": "icon-timeline",
|
||||
"id": 88,
|
||||
"code": 921894,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 116,
|
||||
@ -786,7 +794,7 @@
|
||||
"name": "icon-timer-v1.5",
|
||||
"prevSize": 24,
|
||||
"code": 921895,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 11,
|
||||
@ -794,7 +802,7 @@
|
||||
"name": "icon-topic",
|
||||
"id": 6,
|
||||
"code": 921896,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 115,
|
||||
@ -802,7 +810,7 @@
|
||||
"name": "icon-box-with-dashed-lines",
|
||||
"id": 29,
|
||||
"code": 921897,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@ -1997,7 +2005,7 @@
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M1024 460.8v-460.8l-175.8 175.8c-85.2-69.6-190.8-107.6-302-107.6-127.6 0-247.6 49.8-338 140s-140 210.4-140 338 49.8 247.6 140 338 210.4 140 338 140 247.6-49.8 338-140c74-74 120.8-167.8 135-269.6h-138.6c-32 155.4-169.8 272.8-334.6 272.8-188.2 0-341.4-153.2-341.4-341.4s153.4-341.2 341.6-341.2c76.8 0 147.6 25.4 204.8 68.2l-187.8 187.8h460.8z"
|
||||
"M960 432v-432l-164.8 164.8c-79.8-65.2-178.8-100.8-283.2-100.8-119.6 0-232.2 46.6-316.8 131.2s-131.2 197.2-131.2 316.8 46.6 232.2 131.2 316.8c84.6 84.6 197.2 131.2 316.8 131.2s232.2-46.6 316.8-131.2c69.4-69.4 113.2-157.4 126.6-252.8h-130c-29.8 145.8-159 256-313.6 256-176.4 0-320-143.6-320-320s143.8-320 320.2-320c72 0 138.4 23.8 192 64l-176 176h432z"
|
||||
],
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
@ -2144,6 +2152,37 @@
|
||||
"1161751207457516161751": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"paths": [
|
||||
"M795.2 164.8c-79.8-65.2-178.8-100.8-283.2-100.8-119.6 0-232.2 46.6-316.8 131.2-69.4 69.4-113.2 157.4-126.6 252.8h130c29.6-145.8 158.8-256 313.4-256 72 0 138.4 23.8 192 64l-176 176h432v-432l-164.8 164.8z",
|
||||
"M512 832c-72 0-138.4-23.8-192-64l176-176h-432v432l164.8-164.8c79.8 65.2 178.8 100.8 283.2 100.8 119.6 0 232.2-46.6 316.8-131.2 69.4-69.4 113.2-157.4 126.6-252.8h-130c-29.6 145.8-158.8 256-313.4 256z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-resync"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM832 704l-128 128-192-192-192 192-128-128 192-192-192-192 128-128 192 192 192-192 128 128-192 192 192 192z"
|
||||
|
Binary file not shown.
@ -65,17 +65,18 @@
|
||||
<glyph unicode="󡀦" glyph-name="icon-plot-resource" d="M255.884 256c0.040 0.034 0.082 0.074 0.116 0.116v127.884c0 70.58 57.42 128 128 128h255.884c0.040 0.034 0.082 0.074 0.116 0.116v127.884c0 70.58 57.42 128 128 128h143.658c-93.832 117.038-237.98 192-399.658 192-282.77 0-512-229.23-512-512 0-67.904 13.25-132.704 37.256-192h218.628zM768.116 640c-0.040-0.034-0.082-0.074-0.116-0.116v-127.884c0-70.58-57.42-128-128-128h-255.884c-0.040-0.034-0.082-0.074-0.116-0.116v-127.884c0-70.58-57.42-128-128-128h-143.658c93.832-117.038 237.98-192 399.658-192 282.77 0 512 229.23 512 512 0 67.904-13.25 132.704-37.256 192h-218.628z" />
|
||||
<glyph unicode="󡀧" glyph-name="icon-pointer-left" horiz-adv-x="512" d="M510-64l-256 512 256 512h-256l-256-512 256-512z" />
|
||||
<glyph unicode="󡀨" glyph-name="icon-pointer-right" horiz-adv-x="512" d="M-2 960l256-512-256-512h256l256 512-256 512z" />
|
||||
<glyph unicode="󡀩" glyph-name="icon-refresh" d="M1024 499.2v460.8l-175.8-175.8c-85.2 69.6-190.8 107.6-302 107.6-127.6 0-247.6-49.8-338-140s-140-210.4-140-338 49.8-247.6 140-338 210.4-140 338-140 247.6 49.8 338 140c74 74 120.8 167.8 135 269.6h-138.6c-32-155.4-169.8-272.8-334.6-272.8-188.2 0-341.4 153.2-341.4 341.4s153.4 341.2 341.6 341.2c76.8 0 147.6-25.4 204.8-68.2l-187.8-187.8h460.8z" />
|
||||
<glyph unicode="󡀩" glyph-name="icon-refresh" d="M960 528v432l-164.8-164.8c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2s-131.2-197.2-131.2-316.8 46.6-232.2 131.2-316.8c84.6-84.6 197.2-131.2 316.8-131.2s232.2 46.6 316.8 131.2c69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.8-145.8-159-256-313.6-256-176.4 0-320 143.6-320 320s143.8 320 320.2 320c72 0 138.4-23.8 192-64l-176-176h432z" />
|
||||
<glyph unicode="󡀰" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
|
||||
<glyph unicode="󡀱" glyph-name="icon-sine" d="M1022.294 448c-1.746 7.196-3.476 14.452-5.186 21.786-20.036 85.992-53.302 208.976-98 306.538-22.42 48.938-45.298 86.556-69.946 115.006-48.454 55.93-98.176 67.67-131.356 67.67s-82.902-11.74-131.356-67.672c-24.648-28.45-47.528-66.068-69.948-115.006-44.696-97.558-77.962-220.544-98-306.538-21.646-92.898-46.444-175.138-71.71-237.836-16.308-40.46-30.222-66.358-40.6-82.604-10.378 16.246-24.292 42.142-40.6 82.604-23.272 57.75-46.144 132.088-66.524 216.052h-197.362c1.746-7.196 3.476-14.452 5.186-21.786 20.036-85.992 53.302-208.976 98-306.538 22.42-48.938 45.298-86.556 69.946-115.006 48.454-55.932 98.176-67.672 131.356-67.672s82.902 11.74 131.356 67.672c24.648 28.45 47.528 66.068 69.948 115.006 44.696 97.558 77.962 220.544 98 306.538 21.646 92.898 46.444 175.138 71.71 237.836 16.308 40.46 30.222 66.358 40.6 82.604 10.378-16.246 24.292-42.142 40.6-82.604 23.274-57.748 46.146-132.086 66.526-216.050h197.36z" />
|
||||
<glyph unicode="󡀲" glyph-name="icon-T" d="M0 960v-256h128v64h256v-704h-192v-128h640v128h-192v704h256v-64h128v256z" />
|
||||
<glyph unicode="󡀳" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
|
||||
<glyph unicode="󡀴" glyph-name="icon-two-parts-both" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM128 832h320v-768h-320v768zM896 64h-320v768h320v-768z" />
|
||||
<glyph unicode="󡀵" glyph-name="icon-two-parts-one-only" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-320v768h320v-768z" />
|
||||
<glyph unicode="󡀶" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
|
||||
<glyph unicode="󡀸" glyph-name="icon-brightness" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l191.586-30.216c8.145 56.552 29.998 106.879 62.068 149.006zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l154.18 117.665c-31.476 41.347-53.309 91.675-61.231 146.504zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l117.665-154.18c41.347 31.476 91.675 53.309 146.504 61.231zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-30.216-191.586c56.552-8.145 106.879-29.998 149.006-62.068zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l-154.18-117.665c31.476-41.347 53.309-91.675 61.231-146.504zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l30.216 191.586c-56.552 8.145-106.879 29.998-149.006 62.068zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l-191.586 30.216c-8.125-56.564-29.966-106.906-62.028-149.049zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-117.665 154.18c-41.347-31.476-91.675-53.309-146.504-61.231zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
|
||||
<glyph unicode="󡀹" glyph-name="icon-contrast" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001l0.055 768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52z" />
|
||||
<glyph unicode="󡁀" glyph-name="icon-reset" d="M460.8 499.2l-187.8 187.8c57.2 42.8 128 68.2 204.8 68.2 188.2 0 341.6-153.2 341.6-341.4s-153.2-341.2-341.4-341.2c-165 0-302.8 117.6-334.6 273h-138.4c14.2-101.8 61-195.6 135-269.6 90.2-90.2 210.4-140 338-140s247.6 49.8 338 140 140 210.4 140 338-49.8 247.6-140 338-210.4 140-338 140c-111.4 0-217-38-302-107.6l-176 175.6v-460.8h460.8z" />
|
||||
<glyph unicode="󡀶" glyph-name="icon-x-in-circle" d="M795.2 795.2c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2-69.4-69.4-113.2-157.4-126.6-252.8h130c29.6 145.8 158.8 256 313.4 256 72 0 138.4-23.8 192-64l-176-176h432v432l-164.8-164.8zM512 128c-72 0-138.4 23.8-192 64l176 176h-432v-432l164.8 164.8c79.8-65.2 178.8-100.8 283.2-100.8 119.6 0 232.2 46.6 316.8 131.2 69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.6-145.8-158.8-256-313.4-256z" />
|
||||
<glyph unicode="󡀷" glyph-name="icon-resync" d="M460.8 499.2l-187.8 187.8c57.2 42.8 128 68.2 204.8 68.2 188.2 0 341.6-153.2 341.6-341.4s-153.2-341.2-341.4-341.2c-165 0-302.8 117.6-334.6 273h-138.4c14.2-101.8 61-195.6 135-269.6 90.2-90.2 210.4-140 338-140s247.6 49.8 338 140 140 210.4 140 338-49.8 247.6-140 338-210.4 140-338 140c-111.4 0-217-38-302-107.6l-176 175.6v-460.8h460.8z" />
|
||||
<glyph unicode="󡀸" glyph-name="icon-brightness" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
|
||||
<glyph unicode="󡀹" glyph-name="icon-contrast" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l191.586-30.216c8.145 56.552 29.998 106.879 62.068 149.006zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l154.18 117.665c-31.476 41.347-53.309 91.675-61.231 146.504zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l117.665-154.18c41.347 31.476 91.675 53.309 146.504 61.231zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-30.216-191.586c56.552-8.145 106.879-29.998 149.006-62.068zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l-154.18-117.665c31.476-41.347 53.309-91.675 61.231-146.504zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l30.216 191.586c-56.552 8.145-106.879 29.998-149.006 62.068zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l-191.586 30.216c-8.125-56.564-29.966-106.906-62.028-149.049zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-117.665 154.18c-41.347-31.476-91.675-53.309-146.504-61.231zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
|
||||
<glyph unicode="󡁀" glyph-name="icon-reset" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001l0.055 768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52z" />
|
||||
<glyph unicode="󡄀" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
|
||||
<glyph unicode="󡄁" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
|
||||
<glyph unicode="󡄂" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
|
||||
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
@ -78,7 +78,7 @@ $treeContextTriggerW: 20px;
|
||||
/*************** Tabular */
|
||||
$tabularHeaderH: 22px;
|
||||
$tabularTdPadLR: $itemPadLR;
|
||||
$tabularTdPadTB: 3px;
|
||||
$tabularTdPadTB: 2px;
|
||||
/*************** Imagery */
|
||||
$imageMainControlBarH: 25px;
|
||||
$imageThumbsD: 120px;
|
||||
@ -99,7 +99,7 @@ $plotXBarH: 32px;
|
||||
$plotLegendH: 20px;
|
||||
$plotSwatchD: 8px;
|
||||
// 1: Top, 2: right, 3: bottom, 4: left
|
||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH + $interiorMargin, $plotYBarW);
|
||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
|
||||
/* min plot height is based on user testing to find minimum useful height */
|
||||
$plotMinH: 95px;
|
||||
/*************** Bubbles */
|
||||
|
@ -191,6 +191,19 @@ a.disabled {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.slidable {
|
||||
cursor: move; // Fallback
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
&.horz {
|
||||
cursor: col-resize;
|
||||
}
|
||||
&.vert {
|
||||
cursor: row-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ $glyph-icon-x-in-circle: '\e1036';
|
||||
$glyph-icon-brightness: '\e1038';
|
||||
$glyph-icon-contrast: '\e1039';
|
||||
$glyph-icon-reset: '\e1040';
|
||||
$glyph-icon-resync: '\e1037';
|
||||
$glyph-icon-activity: '\e1100';
|
||||
$glyph-icon-activity-mode: '\e1101';
|
||||
$glyph-icon-autoflow-tabular: '\e1102';
|
||||
@ -179,6 +180,7 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
|
||||
.icon-brightness { @include glyph($glyph-icon-brightness); }
|
||||
.icon-contrast { @include glyph($glyph-icon-contrast); }
|
||||
.icon-reset { @include glyph($glyph-icon-reset); }
|
||||
.icon-resync { @include glyph($glyph-icon-resync); }
|
||||
.icon-activity { @include glyph($glyph-icon-activity); }
|
||||
.icon-activity-mode { @include glyph($glyph-icon-activity-mode); }
|
||||
.icon-autoflow-tabular { @include glyph($glyph-icon-autoflow-tabular); }
|
||||
@ -215,4 +217,4 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
|
||||
.icon-eye-open-12px { @include glyph($glyph-icon-eye-open,'symbolsfont-12px'); }
|
||||
.icon-collapse-pane-left-12px { @include glyph($glyph-icon-collapse-pane-left,'symbolsfont-12px'); }
|
||||
.icon-collapse-pane-right-12px { @include glyph($glyph-icon-collapse-pane-right,'symbolsfont-12px'); }
|
||||
.icon-folder-12px { @include glyph($glyph-icon-folder,'symbolsfont-12px'); }
|
||||
.icon-folder-12px { @include glyph($glyph-icon-folder,'symbolsfont-12px'); }
|
||||
|
@ -363,14 +363,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin webkitProp($name, $val) {
|
||||
#{$name}: #{$val};
|
||||
-webkit-#{$name}: #{$val};
|
||||
}
|
||||
|
||||
@mixin webkitVal($name, $val) {
|
||||
#{$name}: #{$val};
|
||||
#{$name}: -webkit-#{$val};
|
||||
@mixin cursorGrab() {
|
||||
cursor: grab;
|
||||
cursor: -webkit-grab;
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin verticalCenter {
|
||||
@ -392,6 +391,14 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin reverseEllipsis() {
|
||||
direction: rtl;
|
||||
unicode-bidi:bidi-override;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@mixin scrollH($showBar: auto) {
|
||||
overflow-x: $showBar;
|
||||
overflow-y: hidden;
|
||||
|
@ -661,3 +661,4 @@ body.desktop {
|
||||
background: $scrollbarTrackColorBg;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,13 +51,9 @@ table {
|
||||
|
||||
tbody, .tbody {
|
||||
display: table-row-group;
|
||||
tr, .tr {
|
||||
&:hover {
|
||||
background: rgba($colorTabBodyFg, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
tr, .tr {
|
||||
border-top: 1px solid $colorTabBorder;
|
||||
display: table-row;
|
||||
&:first-child .td {
|
||||
border-top: none;
|
||||
@ -71,11 +67,12 @@ table {
|
||||
}
|
||||
th, .th, td, .td {
|
||||
display: table-cell;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
th, .th {
|
||||
border-left: 1px solid $colorTabHeaderBorder;
|
||||
color: $colorTabHeaderFg;
|
||||
padding: $tabularTdPadLR $tabularTdPadLR;
|
||||
padding: $tabularTdPadTB $tabularTdPadLR;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
||||
&:first-child {
|
||||
@ -99,7 +96,6 @@ table {
|
||||
}
|
||||
}
|
||||
td, .td {
|
||||
border-bottom: 1px solid $colorTabBorder;
|
||||
min-width: 20px;
|
||||
color: $colorTelemFresh;
|
||||
padding: $tabularTdPadTB $tabularTdPadLR;
|
||||
|
@ -46,18 +46,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-wrapper-display-area-and-x-axis {
|
||||
// Holds the plot area and the X-axis only
|
||||
position: absolute;
|
||||
top: nth($plotDisplayArea, 1);
|
||||
right: nth($plotDisplayArea, 2);
|
||||
bottom: 0;
|
||||
left: nth($plotDisplayArea, 4);
|
||||
|
||||
.gl-plot-display-area {
|
||||
//@include test(yellow);
|
||||
@if $colorPlotBg != none {
|
||||
background-color: $colorPlotBg;
|
||||
}
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: nth($plotDisplayArea, 3);
|
||||
left: 0;
|
||||
cursor: crosshair;
|
||||
border: 1px solid $colorPlotAreaBorder;
|
||||
}
|
||||
|
||||
.gl-plot-axis-area.gl-plot-x {
|
||||
//@include test(green);
|
||||
top: auto;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: $plotXBarH;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-axis-area {
|
||||
//@include test();
|
||||
position: absolute;
|
||||
&.gl-plot-x {
|
||||
top: auto;
|
||||
right: 0;
|
||||
bottom: $interiorMargin;
|
||||
left: $plotYBarW;
|
||||
height: $plotXBarH;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
&.gl-plot-y {
|
||||
top: $plotLegendH + $interiorMargin;
|
||||
right: auto;
|
||||
@ -84,19 +108,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-display-area {
|
||||
@if $colorPlotBg != none {
|
||||
background-color: $colorPlotBg;
|
||||
}
|
||||
position: absolute;
|
||||
top: nth($plotDisplayArea, 1);
|
||||
right: nth($plotDisplayArea, 2);
|
||||
bottom: nth($plotDisplayArea, 3);
|
||||
left: nth($plotDisplayArea, 4);
|
||||
cursor: crosshair;
|
||||
border: 1px solid $colorPlotAreaBorder;
|
||||
}
|
||||
|
||||
.gl-plot-label,
|
||||
.l-plot-label {
|
||||
color: $colorPlotLabelFg;
|
||||
@ -265,13 +276,9 @@
|
||||
|
||||
.gl-plot-tick,
|
||||
.tick-label {
|
||||
direction: rtl;
|
||||
unicode-bidi:bidi-override;
|
||||
@include reverseEllipsis();
|
||||
font-size: 0.7rem;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
&.gl-plot-x-tick-label,
|
||||
&.tick-label-x {
|
||||
right: auto;
|
||||
|
@ -62,7 +62,7 @@ $colorCreateBtn: $colorKey;
|
||||
$colorGridLines: rgba(#fff, 0.05);
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
||||
$colorObjHdrIc: lighten($colorObjHdrTxt, 20%);
|
||||
$colorTick: rgba(white, 0.2);
|
||||
$colorSelectableSelectedPrimary: $colorKey;
|
||||
$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
|
||||
@ -158,11 +158,11 @@ $shdwItemText: rgba(black, 0.1) 0 1px 2px;
|
||||
$colorItemBgSelected: $colorKey;
|
||||
|
||||
// Tabular
|
||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||
$colorTabBorder: pullForward($colorBodyBg, 5%);
|
||||
$colorTabBodyBg: darken($colorBodyBg, 10%);
|
||||
$colorTabBodyFg: lighten($colorTabBodyBg, 40%);
|
||||
$colorTabHeaderBg: rgba(white, 0.1); // lighten($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: $colorBodyFg; //lighten($colorTabHeaderBg, 40%);
|
||||
$colorTabHeaderBg: rgba(white, 0.1);
|
||||
$colorTabHeaderFg: $colorBodyFg;
|
||||
$colorTabHeaderBorder: $colorBodyBg;
|
||||
|
||||
// Plot
|
||||
|
@ -62,7 +62,7 @@ $colorCreateBtn: $colorKey;
|
||||
$colorGridLines: rgba(#000, 0.05);
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
||||
$colorObjHdrIc: lighten($colorObjHdrTxt, 30%);
|
||||
$colorTick: rgba(black, 0.2);
|
||||
$colorSelectableSelectedPrimary: $colorKey;
|
||||
$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
|
||||
|
@ -23,31 +23,20 @@
|
||||
define([
|
||||
"./src/ConductorTelemetryDecorator",
|
||||
"./src/ConductorRepresenter",
|
||||
"./src/ConductorService",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
ConductorTelemetryDecorator,
|
||||
ConductorRepresenter,
|
||||
ConductorService,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/features/conductor-v2/compatibility", {
|
||||
"extensions": {
|
||||
"services": [
|
||||
{
|
||||
"key": "conductorService",
|
||||
"implementation": ConductorService,
|
||||
"depends": [
|
||||
"timeConductor"
|
||||
]
|
||||
}
|
||||
],
|
||||
"representers": [
|
||||
{
|
||||
"implementation": ConductorRepresenter,
|
||||
"depends": [
|
||||
"timeConductor"
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -57,7 +46,7 @@ define([
|
||||
"provides": "telemetryService",
|
||||
"implementation": ConductorTelemetryDecorator,
|
||||
"depends": [
|
||||
"timeConductor"
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -37,11 +37,11 @@ define(
|
||||
* @constructor
|
||||
*/
|
||||
function ConductorRepresenter(
|
||||
timeConductor,
|
||||
openmct,
|
||||
scope,
|
||||
element
|
||||
) {
|
||||
this.conductor = timeConductor;
|
||||
this.conductor = openmct.conductor;
|
||||
this.scope = scope;
|
||||
this.element = element;
|
||||
|
||||
|
@ -1,72 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
|
||||
], function (
|
||||
|
||||
) {
|
||||
|
||||
function Conductor(timeConductorService) {
|
||||
this.timeConductor = timeConductorService.conductor();
|
||||
}
|
||||
|
||||
Conductor.prototype.displayStart = function () {
|
||||
return this.timeConductor.bounds().start;
|
||||
};
|
||||
|
||||
Conductor.prototype.displayEnd = function () {
|
||||
return this.timeConductor.bounds().end;
|
||||
};
|
||||
|
||||
Conductor.prototype.domainOptions = function () {
|
||||
throw new Error([
|
||||
'domainOptions not implemented in compatibility layer,',
|
||||
' you must be using some crazy unknown code'
|
||||
].join(''));
|
||||
};
|
||||
|
||||
Conductor.prototype.domain = function () {
|
||||
var system = this.timeConductor.timeSystem();
|
||||
return {
|
||||
key: system.metadata.key,
|
||||
name: system.metadata.name,
|
||||
format: system.formats()[0]
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Small compatibility layer that implements old conductor service by
|
||||
* wrapping new time conductor. This allows views that previously depended
|
||||
* directly on the conductor service to continue to do so without
|
||||
* modification.
|
||||
*/
|
||||
function ConductorService(timeConductor) {
|
||||
this.tc = new Conductor(timeConductor);
|
||||
}
|
||||
|
||||
ConductorService.prototype.getConductor = function () {
|
||||
return this.tc;
|
||||
};
|
||||
|
||||
return ConductorService;
|
||||
});
|
@ -36,8 +36,8 @@ define(
|
||||
* the service which exposes the global time conductor
|
||||
* @param {TelemetryService} telemetryService the decorated service
|
||||
*/
|
||||
function ConductorTelemetryDecorator(timeConductor, telemetryService) {
|
||||
this.conductor = timeConductor;
|
||||
function ConductorTelemetryDecorator(openmct, telemetryService) {
|
||||
this.conductor = openmct.conductor;
|
||||
this.telemetryService = telemetryService;
|
||||
|
||||
this.amendRequests = ConductorTelemetryDecorator.prototype.amendRequests.bind(this);
|
||||
|
@ -23,37 +23,39 @@
|
||||
define([
|
||||
"./src/ui/TimeConductorViewService",
|
||||
"./src/ui/TimeConductorController",
|
||||
"./src/TimeConductor",
|
||||
"./src/ui/ConductorAxisController",
|
||||
"./src/ui/ConductorTOIController",
|
||||
"./src/ui/TimeOfInterestController",
|
||||
"./src/ui/MctConductorAxis",
|
||||
"./src/ui/NumberFormat",
|
||||
"text!./res/templates/time-conductor.html",
|
||||
"text!./res/templates/mode-selector/mode-selector.html",
|
||||
"text!./res/templates/mode-selector/mode-menu.html",
|
||||
'legacyRegistry'
|
||||
"text!./res/templates/time-of-interest.html",
|
||||
"legacyRegistry"
|
||||
], function (
|
||||
TimeConductorViewService,
|
||||
TimeConductorController,
|
||||
TimeConductor,
|
||||
ConductorAxisController,
|
||||
ConductorTOIController,
|
||||
TimeOfInterestController,
|
||||
MCTConductorAxis,
|
||||
NumberFormat,
|
||||
timeConductorTemplate,
|
||||
modeSelectorTemplate,
|
||||
modeMenuTemplate,
|
||||
timeOfInterest,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/features/conductor-v2/conductor", {
|
||||
"extensions": {
|
||||
"services": [
|
||||
{
|
||||
"key": "timeConductor",
|
||||
"implementation": TimeConductor
|
||||
},
|
||||
{
|
||||
"key": "timeConductorViewService",
|
||||
"implementation": TimeConductorViewService,
|
||||
"depends": [
|
||||
"timeConductor",
|
||||
"openmct",
|
||||
"timeSystems[]"
|
||||
]
|
||||
}
|
||||
@ -65,9 +67,29 @@ define([
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$window",
|
||||
"timeConductor",
|
||||
"openmct",
|
||||
"timeConductorViewService",
|
||||
"timeSystems[]"
|
||||
"timeSystems[]",
|
||||
"formatService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "ConductorTOIController",
|
||||
"implementation": ConductorTOIController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"openmct",
|
||||
"timeConductorViewService",
|
||||
"formatService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "TimeOfInterestController",
|
||||
"implementation": TimeOfInterestController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"openmct",
|
||||
"formatService"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -76,7 +98,7 @@ define([
|
||||
"key": "mctConductorAxis",
|
||||
"implementation": MCTConductorAxis,
|
||||
"depends": [
|
||||
"timeConductor",
|
||||
"openmct",
|
||||
"formatService"
|
||||
]
|
||||
}
|
||||
@ -103,6 +125,10 @@ define([
|
||||
{
|
||||
"key": "mode-selector",
|
||||
"template": modeSelectorTemplate
|
||||
},
|
||||
{
|
||||
"key": "time-of-interest",
|
||||
"template": timeOfInterest
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
|
@ -1,3 +1,11 @@
|
||||
$ueTimeConductorH: (25px, 3px, 20px);
|
||||
$ueTimeConductorH: (25px, 16px, 20px); // Heights for Ticks, Data Visualization, Controls elements
|
||||
$ueTimeConductorRtH: (25px, 3px, 20px); // Heights for elements in Real-time mode
|
||||
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
|
||||
$timeCondInputDeltaDefW: 60px; // Default width for delta value inputs, typically 00:00:00
|
||||
$timeCondInputDeltaDefW: 60px; // Default width for delta value inputs, typically 00:00:00
|
||||
$timeCondTOIIconD: 12px; // height and width of icon used for TOI indicator
|
||||
$timeCondTOIValOffset: 0px;
|
||||
$ticksBlockerFadeW: 50px;
|
||||
$toiBlockerFadeW: 10px;
|
||||
$toiH: 12px; // Needs to be an even number to avoid sub-pixel antialiasing of the vertical line
|
||||
$toiPad: 4px;
|
||||
$timeCondAxisLROffset: (($toiH / 2) + $toiPad, ($toiH / 2) + $toiPad); // Margin to left, right of tick axis and vis bar. For paging, use 15, 20px
|
@ -54,7 +54,7 @@
|
||||
// Clock hands
|
||||
div[class*="hand"] {
|
||||
$handW: 2px;
|
||||
$handH: $d * 0.4; //8px;
|
||||
$handH: $d * 0.4;
|
||||
@include transform(translate(-50%, -50%));
|
||||
@include animation-iteration-count(infinite);
|
||||
@include animation-timing-function(linear);
|
||||
@ -125,7 +125,6 @@
|
||||
}
|
||||
|
||||
.l-time-conductor-inputs-holder {
|
||||
$ticksBlockerFadeW: 50px;
|
||||
$iconCalendarW: 16px;
|
||||
$wBgColor: $colorBodyBg;
|
||||
|
||||
@ -136,6 +135,7 @@
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
.l-time-range-w {
|
||||
// Wraps a datetime text input field
|
||||
height: 100%;
|
||||
@ -159,6 +159,9 @@
|
||||
content: 'End';
|
||||
}
|
||||
}
|
||||
.l-time-conductor-inputs {
|
||||
pointer-events: auto;
|
||||
}
|
||||
input[type="text"] {
|
||||
@include trans-prop-nice(padding, 250ms);
|
||||
}
|
||||
@ -175,7 +178,7 @@
|
||||
}
|
||||
|
||||
.l-time-conductor-inputs-and-ticks {
|
||||
$c: $colorTimeCondTicks; //$colorTick;
|
||||
$c: $colorTimeCondTicks;
|
||||
height: $r1H;
|
||||
mct-conductor-axis {
|
||||
display: block;
|
||||
@ -184,8 +187,9 @@
|
||||
}
|
||||
.l-axis-holder {
|
||||
height: $r1H;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: nth($timeCondAxisLROffset, 1);
|
||||
right: nth($timeCondAxisLROffset, 2);
|
||||
svg {
|
||||
text-rendering: geometricPrecision;
|
||||
width: 100%;
|
||||
@ -208,9 +212,49 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.l-data-visualization {
|
||||
background: $colorTimeCondDataVisBg;
|
||||
.l-data-visualization-holder {
|
||||
height: $r2H;
|
||||
z-index: 2; // Must lift above ticks and inputs
|
||||
|
||||
.l-page-button,
|
||||
.l-data-visualization {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.l-page-button {
|
||||
@if nth($timeCondAxisLROffset, 1) + nth($timeCondAxisLROffset, 2) > 30 {
|
||||
left: 0;
|
||||
width: nth($timeCondAxisLROffset, 1);
|
||||
&.align-right {
|
||||
left: auto;
|
||||
right: 0;
|
||||
width: nth($timeCondAxisLROffset, 2);
|
||||
}
|
||||
} @else {
|
||||
// Hide these if the offsets aren't enough
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.l-data-visualization {
|
||||
background: $colorTimeCondDataVisBg;
|
||||
left: nth($timeCondAxisLROffset, 1);
|
||||
right: nth($timeCondAxisLROffset, 2);
|
||||
&:hover {
|
||||
.l-toi-holder.hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.l-toi-holder.pinned.active {
|
||||
opacity: 0.4;
|
||||
.l-toi-val {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-time-conductor-controls {
|
||||
@ -219,13 +263,11 @@
|
||||
.l-time-conductor-zoom-w {
|
||||
@include justify-content(flex-end);
|
||||
.time-conductor-zoom {
|
||||
display: none; // TEMP per request from Andrew 8/1/16
|
||||
height: $r3H;
|
||||
min-width: 100px;
|
||||
width: 20%;
|
||||
}
|
||||
.time-conductor-zoom-current-range {
|
||||
display: none; // TEMP per request from Andrew 8/1/16
|
||||
color: $colorTick;
|
||||
}
|
||||
}
|
||||
@ -235,7 +277,9 @@
|
||||
&.realtime-mode,
|
||||
&.lad-mode {
|
||||
.time-conductor-icon {
|
||||
&:before { color: $colorTimeCondKeyBg; }
|
||||
&:before {
|
||||
color: $colorTimeCondKeyBg;
|
||||
}
|
||||
div[class*="hand"] {
|
||||
@include animation-name(clock-hands);
|
||||
&:before {
|
||||
@ -276,13 +320,16 @@
|
||||
}
|
||||
|
||||
.l-data-visualization {
|
||||
background: $colorTimeCondDataVisRtBg !important
|
||||
background: $colorTimeCondDataVisRtBg !important;
|
||||
}
|
||||
|
||||
.mode-selector .s-menu-button {
|
||||
$fg: $colorTimeCondKeyFg;
|
||||
@include btnSubtle($bg: $colorTimeCondKeyBg, $bgHov: pullForward($colorTimeCondKeyBg, $ltGamma), $fg: $colorTimeCondKeyFg);
|
||||
&:before { color: $fg !important; };
|
||||
&:before {
|
||||
color: $fg !important;
|
||||
}
|
||||
;
|
||||
color: $fg !important;
|
||||
}
|
||||
}
|
||||
@ -298,6 +345,9 @@
|
||||
.mode-selector .s-menu-button:before {
|
||||
content: $i;
|
||||
}
|
||||
.l-axis-holder {
|
||||
@include cursorGrab();
|
||||
}
|
||||
}
|
||||
|
||||
// Realtime mode
|
||||
@ -344,7 +394,9 @@
|
||||
/******************************************************************** MOBILE */
|
||||
|
||||
@include phoneandtablet {
|
||||
.l-time-conductor-holder { min-width: 0 !important; }
|
||||
.l-time-conductor-holder {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
.super-menu.mini {
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
|
@ -0,0 +1,271 @@
|
||||
/*****************************************************************************
|
||||
* 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-include.l-toi-holder,
|
||||
.l-toi-holder:after,
|
||||
.l-toi-holder:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
mct-include.l-toi-holder {
|
||||
$blockerFadeW: $toiBlockerFadeW;
|
||||
@include transform(translateX(-50%));
|
||||
color: $toiColorBg;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: $toiH;
|
||||
&:not(.pinned) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.pinned {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
// Vertical lines. TC uses both; plot only uses :before
|
||||
border-left: 1px dashed $toiColorBg;
|
||||
box-sizing: border-box;
|
||||
content: '';
|
||||
display: block;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.l-toi {
|
||||
// Holds buttons and val. Acts as a blocking element.
|
||||
@include background-image(linear-gradient(90deg, transparent, $toiColorBlocker 10%, $toiColorBlocker 90%, transparent 100%));
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
box-sizing: content-box;
|
||||
height: $toiH;
|
||||
left: $toiPad * -2;
|
||||
@include transform(translateY(-50%)); top: 50%;
|
||||
padding: $toiPad;
|
||||
z-index: 1;
|
||||
|
||||
.l-toi-buttons {
|
||||
@include trans-prop-nice($props: (width, padding), $dur: 250ms);
|
||||
border-radius: $controlCr;
|
||||
box-sizing: content-box;
|
||||
font-size: $toiH;
|
||||
height: 100%;
|
||||
line-height: $toiH;
|
||||
padding: $toiPad;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
justify-content: space-between;
|
||||
width: $toiH;
|
||||
|
||||
&:hover {
|
||||
// Expand and display controls; clock icon changes to resync
|
||||
background-color: $toiColorBg;
|
||||
cursor: pointer;
|
||||
width: 30px;
|
||||
|
||||
.icon-button {
|
||||
color: rgba($toiColorCtrlFg, 0.5);
|
||||
opacity: 1;
|
||||
&:hover {
|
||||
color: $toiColorCtrlFg;
|
||||
}
|
||||
}
|
||||
|
||||
.t-button-resync {
|
||||
order: 1;
|
||||
&:before {
|
||||
content: $glyph-icon-resync;
|
||||
}
|
||||
}
|
||||
.t-button-unpin {
|
||||
order: 2;
|
||||
&:hover {
|
||||
color: $toiColorBgAlert;
|
||||
}
|
||||
}
|
||||
|
||||
& + .l-toi-val {
|
||||
// Dim the value to emphasize the controls
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
color: $toiColorBg;
|
||||
}
|
||||
|
||||
.t-button-resync {
|
||||
@extend .icon-clock;
|
||||
&:hover { color: $toiColorCtrlFg; }
|
||||
}
|
||||
|
||||
.t-button-unpin {
|
||||
@include trans-prop-nice($props: opacity, $dur: 150ms);
|
||||
@extend .icon-x-in-circle;
|
||||
float: right;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.l-toi-val {
|
||||
display: none; // Hide by default; see .show-val below
|
||||
}
|
||||
|
||||
// TOI is showing value as well
|
||||
&.show-val {
|
||||
.l-toi {
|
||||
.l-toi-buttons {
|
||||
order: 1;
|
||||
&:hover {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
.l-toi-val {
|
||||
@include trans-prop-nice($props: opacity, $dur: 150ms);
|
||||
background-color: $toiColorBg;
|
||||
border-radius: $controlCr;
|
||||
box-sizing: content-box;
|
||||
color: $toiColorFg;
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 400;
|
||||
height: $toiH;
|
||||
line-height: $toiH;
|
||||
order: 2;
|
||||
padding: 1px 3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&.val-to-left {
|
||||
.l-toi {
|
||||
left: auto;
|
||||
right: $toiPad * -2;
|
||||
|
||||
.l-toi-buttons {
|
||||
order: 2;
|
||||
&:hover {
|
||||
.t-button-resync { order: 2; }
|
||||
.t-button-unpin { order: 1; }
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.l-toi-val {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TOI in tables
|
||||
.tabular,
|
||||
table {
|
||||
tbody, .tbody {
|
||||
tr, .tr {
|
||||
&.l-toi-tablerow {
|
||||
border-top: 1px dashed $toiColorBg;
|
||||
z-index: 1;
|
||||
td, .td {
|
||||
.l-toi-holder {
|
||||
left: 50% !important;
|
||||
&:before,
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
.l-toi {
|
||||
background: rgba($toiColorBlocker, 0.9);
|
||||
border-radius: 20%;
|
||||
height: auto;
|
||||
padding: $toiPad;
|
||||
@include transform(translate(-50%, -50%));
|
||||
left: 50%; right: auto; top: 0;
|
||||
.l-toi-buttons {
|
||||
padding: 1px;
|
||||
&:hover {
|
||||
padding: $toiPad;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TOI in plots
|
||||
.gl-plot {
|
||||
.gl-plot-wrapper-display-area-and-x-axis {
|
||||
right: nth($plotDisplayArea, 2) + ($toiH / 2) + $toiPad; // Make room for TOI element
|
||||
|
||||
.l-toi-holder {
|
||||
bottom: nth($plotDisplayArea, 3) - $interiorMargin;
|
||||
z-index: 3;
|
||||
|
||||
.l-toi {
|
||||
@include transform(translateY(100%));
|
||||
|
||||
}
|
||||
|
||||
.l-toi {
|
||||
top: auto;
|
||||
bottom: $toiPad;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TOI in Time Conductor
|
||||
.l-time-conductor {
|
||||
.l-toi-holder {
|
||||
$linesVOffset: 2px;
|
||||
&:before,
|
||||
&:after {
|
||||
// Vertical lines
|
||||
border-left-style: solid;
|
||||
height: ((nth($ueTimeConductorH, 2) - $timeCondTOIIconD)/2) + $linesVOffset;
|
||||
}
|
||||
|
||||
&:before {
|
||||
@include transform(translate(-50%, $linesVOffset * -1));
|
||||
top: 0px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
&:after {
|
||||
@include transform(translate(-50%, $linesVOffset));
|
||||
top: auto;
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,17 @@
|
||||
$colorTimeCondTicks: pullForward($colorBodyBg, 30%);
|
||||
$colorTimeCondKeyBg: #4e70dc;
|
||||
$colorTimeCondKeyFg: #fff;
|
||||
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
|
||||
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 5%);
|
||||
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 10%);
|
||||
@import "time-conductor-base";
|
||||
|
||||
// Time of Interest
|
||||
$toiColorBg: #6b93c6;
|
||||
$toiColorBlocker: $colorBodyBg; // Color of blocker element beneath the TOI icons
|
||||
$toiColorFg: #000; // Used by value display
|
||||
$toiColorCtrlFg: #fff;
|
||||
$toiColorBgAlert: #cf2a12; // Used by unpin button on hover
|
||||
$colorTimeCondTOIBg: darken($toiColorBg, 20%);
|
||||
$colorTimeCondTOIBgHov: $toiColorBg;
|
||||
|
||||
@import "time-conductor-base";
|
||||
@import "time-of-interest";
|
@ -36,4 +36,15 @@ $colorTimeCondKeyBg: #6178dc;
|
||||
$colorTimeCondKeyFg: #fff;
|
||||
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
|
||||
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 30%);
|
||||
@import "time-conductor-base";
|
||||
|
||||
// Time of Interest
|
||||
$toiColorBg: #6b93c6;
|
||||
$toiColorBlocker: $colorBodyBg; // Color of blocker element beneath the TOI icons
|
||||
$toiColorFg: #fff; // Used by value display
|
||||
$toiColorCtrlFg: #fff;
|
||||
$toiColorBgAlert: #ff9540; // Used by unpin button on hover
|
||||
$colorTimeCondTOIBg: darken($toiColorBg, 20%);
|
||||
$colorTimeCondTOIBgHov: $toiColorBg;
|
||||
|
||||
@import "time-conductor-base";
|
||||
@import "time-of-interest";
|
@ -1,6 +1,7 @@
|
||||
<!-- Parent holder for time conductor. follow-mode | fixed-mode -->
|
||||
<div ng-controller="TimeConductorController as tcController"
|
||||
class="holder grows flex-elem l-flex-row l-time-conductor {{modeModel.selectedKey}}-mode {{timeSystemModel.selected.metadata.key}}-time-system">
|
||||
class="holder grows flex-elem l-flex-row l-time-conductor {{modeModel.selectedKey}}-mode {{timeSystemModel.selected.metadata.key}}-time-system"
|
||||
ng-class="{'status-panning': tcController.panning}">
|
||||
|
||||
<div class="flex-elem holder time-conductor-icon">
|
||||
<div class="hand-little"></div>
|
||||
@ -13,63 +14,67 @@
|
||||
<form class="l-time-conductor-inputs-holder"
|
||||
ng-submit="tcController.updateBoundsFromForm(boundsModel)">
|
||||
<span class="l-time-range-w start-w">
|
||||
<span class="l-time-range-input-w start-date">
|
||||
<span class="title"></span>
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.format,
|
||||
validate: tcController.validation.validateStart
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
|
||||
field="'start'"
|
||||
class="time-range-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
<span class="l-time-range-input-w time-delta start-delta"
|
||||
ng-class="{'hide':(modeModel.selectedKey === 'fixed')}">
|
||||
-
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.deltaFormat,
|
||||
validate: tcController.validation.validateStartDelta
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
|
||||
field="'startDelta'"
|
||||
class="hrs-min-input">
|
||||
</mct-control>
|
||||
<span class="l-time-conductor-inputs">
|
||||
<span class="l-time-range-input-w start-date">
|
||||
<span class="title"></span>
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.format,
|
||||
validate: tcController.validation.validateStart
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
|
||||
field="'start'"
|
||||
class="time-range-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
<span class="l-time-range-input-w time-delta start-delta"
|
||||
ng-class="{'hide':(modeModel.selectedKey === 'fixed')}">
|
||||
-
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.deltaFormat,
|
||||
validate: tcController.validation.validateStartDelta
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
|
||||
field="'startDelta'"
|
||||
class="hrs-min-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="l-time-range-w end-w">
|
||||
<span class="l-time-range-input-w end-date"
|
||||
ng-controller="ToggleController as t2">
|
||||
<span class="title"></span>
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.format,
|
||||
validate: tcController.validation.validateEnd
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
|
||||
ng-disabled="modeModel.selectedKey !== 'fixed'"
|
||||
field="'end'"
|
||||
class="time-range-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
<span class="l-time-range-input-w time-delta end-delta"
|
||||
ng-class="{'hide':(modeModel.selectedKey === 'fixed')}">
|
||||
+
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.deltaFormat,
|
||||
validate: tcController.validation.validateEndDelta
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
|
||||
field="'endDelta'"
|
||||
class="hrs-min-input">
|
||||
</mct-control>
|
||||
<span class="l-time-conductor-inputs">
|
||||
<span class="l-time-range-input-w end-date"
|
||||
ng-controller="ToggleController as t2">
|
||||
<span class="title"></span>
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.format,
|
||||
validate: tcController.validation.validateEnd
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
|
||||
ng-disabled="modeModel.selectedKey !== 'fixed'"
|
||||
field="'end'"
|
||||
class="time-range-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
<span class="l-time-range-input-w time-delta end-delta"
|
||||
ng-class="{'hide':(modeModel.selectedKey === 'fixed')}">
|
||||
+
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{
|
||||
format: timeSystemModel.deltaFormat,
|
||||
validate: tcController.validation.validateEndDelta
|
||||
}"
|
||||
ng-model="boundsModel"
|
||||
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
|
||||
field="'endDelta'"
|
||||
class="hrs-min-input">
|
||||
</mct-control>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@ -78,8 +83,18 @@
|
||||
<mct-conductor-axis></mct-conductor-axis>
|
||||
</div>
|
||||
|
||||
<!-- Holds data availability, time of interest -->
|
||||
<div class="l-data-visualization l-row-elem flex-elem"></div>
|
||||
<!-- Holds data visualization, time of interest -->
|
||||
<div class="l-data-visualization-holder l-row-elem flex-elem"
|
||||
ng-controller="ConductorTOIController as toi">
|
||||
<a class="l-page-button s-icon-button icon-pointer-left"></a>
|
||||
<div class="l-data-visualization" ng-click="toi.setTOIFromPosition($event)">
|
||||
<mct-include key="'time-of-interest'"
|
||||
class="l-toi-holder show-val"
|
||||
ng-class="{ pinned: toi.pinned, 'val-to-left': toi.left > 80 }"
|
||||
ng-style="{'left': toi.left + '%'}"></mct-include>
|
||||
</div>
|
||||
<a class="l-page-button align-right s-icon-button icon-pointer-right"></a>
|
||||
</div>
|
||||
|
||||
<!-- Holds time system and session selectors, and zoom control -->
|
||||
<div class="l-time-conductor-controls l-row-elem l-flex-row flex-elem">
|
||||
@ -98,9 +113,18 @@
|
||||
}">
|
||||
</mct-control>
|
||||
<!-- Zoom control -->
|
||||
<div class="l-time-conductor-zoom-w grows flex-elem l-flex-row">
|
||||
<span class="time-conductor-zoom-current-range flex-elem flex-fixed holder"></span>
|
||||
<input class="time-conductor-zoom flex-elem" type="range" />
|
||||
<div ng-if="tcController.supportsZoom"
|
||||
class="l-time-conductor-zoom-w grows flex-elem l-flex-row">
|
||||
{{currentZoom}}
|
||||
<span
|
||||
class="time-conductor-zoom-current-range flex-elem flex-fixed holder">{{timeUnits}}</span>
|
||||
<input class="time-conductor-zoom flex-elem" type="range"
|
||||
ng-model="tcController.currentZoom"
|
||||
ng-mouseUp="tcController.onZoomStop(tcController.currentZoom)"
|
||||
ng-change="tcController.onZoom(tcController.currentZoom)"
|
||||
min="0.01"
|
||||
step="0.01"
|
||||
max="0.99" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
<div class="abs angular-controller"
|
||||
ng-controller="TimeOfInterestController as toi">
|
||||
<div class="l-flex-row l-toi">
|
||||
<span class="flex-elem l-flex-row l-toi-buttons">
|
||||
<a class="flex-elem t-button-resync icon-button" title="Re-sync Time of Interest"
|
||||
ng-click="toi.resync()"></a>
|
||||
<a class="flex-elem t-button-unpin icon-button" title="Unset Time of Interest"
|
||||
ng-click="toi.dismiss()"></a>
|
||||
</span>
|
||||
<span class="flex-elem l-toi-val">{{toi.toiText}}</span>
|
||||
</div>
|
||||
</div>
|
@ -1,179 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['EventEmitter'], function (EventEmitter) {
|
||||
|
||||
/**
|
||||
* The public API for setting and querying time conductor state. The
|
||||
* time conductor is the means by which the temporal bounds of a view
|
||||
* are controlled. Time-sensitive views will typically respond to
|
||||
* changes to bounds or other properties of the time conductor and
|
||||
* update the data displayed based on the time conductor state.
|
||||
*
|
||||
* The TimeConductor extends the EventEmitter class. A number of events are
|
||||
* fired when properties of the time conductor change, which are
|
||||
* documented below.
|
||||
* @constructor
|
||||
*/
|
||||
function TimeConductor() {
|
||||
EventEmitter.call(this);
|
||||
|
||||
//The Time System
|
||||
this.system = undefined;
|
||||
//The Time Of Interest
|
||||
this.toi = undefined;
|
||||
|
||||
this.boundsVal = {
|
||||
start: undefined,
|
||||
end: undefined
|
||||
};
|
||||
|
||||
//Default to fixed mode
|
||||
this.followMode = false;
|
||||
}
|
||||
|
||||
TimeConductor.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Validate the given bounds. This can be used for pre-validation of
|
||||
* bounds, for example by views validating user inputs.
|
||||
* @param bounds The start and end time of the conductor.
|
||||
* @returns {string | true} A validation error, or true if valid
|
||||
*/
|
||||
TimeConductor.prototype.validateBounds = function (bounds) {
|
||||
if ((bounds.start === undefined) ||
|
||||
(bounds.end === undefined) ||
|
||||
isNaN(bounds.start) ||
|
||||
isNaN(bounds.end)
|
||||
) {
|
||||
return "Start and end must be specified as integer values";
|
||||
} else if (bounds.start > bounds.end) {
|
||||
return "Specified start date exceeds end bound";
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get or set the follow mode of the time conductor. In follow mode the
|
||||
* time conductor ticks, regularly updating the bounds from a timing
|
||||
* source appropriate to the selected time system and mode of the time
|
||||
* conductor.
|
||||
* @fires TimeConductor#follow
|
||||
* @param {boolean} followMode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
TimeConductor.prototype.follow = function (followMode) {
|
||||
if (arguments.length > 0) {
|
||||
this.followMode = followMode;
|
||||
/**
|
||||
* @event TimeConductor#follow The TimeConductor has toggled
|
||||
* into or out of follow mode.
|
||||
* @property {boolean} followMode true if follow mode is
|
||||
* enabled, otherwise false.
|
||||
*/
|
||||
this.emit('follow', this.followMode);
|
||||
}
|
||||
return this.followMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} TimeConductorBounds
|
||||
* @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system
|
||||
* @property {number} end The end time displayed by the time conductor in ms since epoch.
|
||||
*/
|
||||
/**
|
||||
* Get or set the start and end time of the time conductor. Basic validation
|
||||
* of bounds is performed.
|
||||
*
|
||||
* @param {TimeConductorBounds} newBounds
|
||||
* @throws {Error} Validation error
|
||||
* @fires TimeConductor#bounds
|
||||
* @returns {TimeConductorBounds}
|
||||
*/
|
||||
TimeConductor.prototype.bounds = function (newBounds) {
|
||||
if (arguments.length > 0) {
|
||||
var validationResult = this.validateBounds(newBounds);
|
||||
if (validationResult !== true) {
|
||||
throw new Error(validationResult);
|
||||
}
|
||||
//Create a copy to avoid direct mutation of conductor bounds
|
||||
this.boundsVal = JSON.parse(JSON.stringify(newBounds));
|
||||
/**
|
||||
* @event TimeConductor#bounds The start time, end time, or
|
||||
* both have been updated
|
||||
* @property {TimeConductorBounds} bounds
|
||||
*/
|
||||
this.emit('bounds', this.boundsVal);
|
||||
}
|
||||
//Return a copy to prevent direct mutation of time conductor bounds.
|
||||
return JSON.parse(JSON.stringify(this.boundsVal));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get or set the time system of the TimeConductor. Time systems determine
|
||||
* units, epoch, and other aspects of time representation. When changing
|
||||
* the time system in use, new valid bounds must also be provided.
|
||||
* @param {TimeSystem} newTimeSystem
|
||||
* @param {TimeConductorBounds} bounds
|
||||
* @fires TimeConductor#timeSystem
|
||||
* @returns {TimeSystem} The currently applied time system
|
||||
*/
|
||||
TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) {
|
||||
if (arguments.length >= 2) {
|
||||
this.system = newTimeSystem;
|
||||
/**
|
||||
* @event TimeConductor#timeSystem The time system used by the time
|
||||
* conductor has changed. A change in Time System will always be
|
||||
* followed by a bounds event specifying new query bounds
|
||||
* @property {TimeSystem} The value of the currently applied
|
||||
* Time System
|
||||
* */
|
||||
this.emit('timeSystem', this.system);
|
||||
this.bounds(bounds);
|
||||
} else if (arguments.length === 1) {
|
||||
throw new Error('Must set bounds when changing time system');
|
||||
}
|
||||
return this.system;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get or set the Time of Interest. The Time of Interest is the temporal
|
||||
* focus of the current view. It can be manipulated by the user from the
|
||||
* time conductor or from other views.
|
||||
* @fires TimeConductor#timeOfInterest
|
||||
* @param newTOI
|
||||
* @returns {number} the current time of interest
|
||||
*/
|
||||
TimeConductor.prototype.timeOfInterest = function (newTOI) {
|
||||
if (arguments.length > 0) {
|
||||
this.toi = newTOI;
|
||||
/**
|
||||
* @event TimeConductor#timeOfInterest The Time of Interest has moved.
|
||||
* @property {number} Current time of interest
|
||||
*/
|
||||
this.emit('timeOfInterest', this.toi);
|
||||
}
|
||||
return this.toi;
|
||||
};
|
||||
|
||||
return TimeConductor;
|
||||
});
|
@ -1,110 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./TimeConductor'], function (TimeConductor) {
|
||||
describe("The Time Conductor", function () {
|
||||
var tc,
|
||||
timeSystem,
|
||||
bounds,
|
||||
eventListener,
|
||||
toi,
|
||||
follow;
|
||||
|
||||
beforeEach(function () {
|
||||
tc = new TimeConductor();
|
||||
timeSystem = {};
|
||||
bounds = {start: 0, end: 0};
|
||||
eventListener = jasmine.createSpy("eventListener");
|
||||
toi = 111;
|
||||
follow = true;
|
||||
});
|
||||
|
||||
it("Supports setting and querying of time of interest and and follow mode", function () {
|
||||
expect(tc.timeOfInterest()).not.toBe(toi);
|
||||
tc.timeOfInterest(toi);
|
||||
expect(tc.timeOfInterest()).toBe(toi);
|
||||
|
||||
expect(tc.follow()).not.toBe(follow);
|
||||
tc.follow(follow);
|
||||
expect(tc.follow()).toBe(follow);
|
||||
});
|
||||
|
||||
it("Allows setting of valid bounds", function () {
|
||||
bounds = {start: 0, end: 1};
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
|
||||
expect(tc.bounds()).toEqual(bounds);
|
||||
});
|
||||
|
||||
it("Disallows setting of invalid bounds", function () {
|
||||
bounds = {start: 1, end: 0};
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
|
||||
bounds = {start: 1};
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
});
|
||||
|
||||
it("Allows setting of time system with bounds", function () {
|
||||
expect(tc.timeSystem()).not.toBe(timeSystem);
|
||||
expect(tc.timeSystem.bind(tc, timeSystem, bounds)).not.toThrow();
|
||||
expect(tc.timeSystem()).toBe(timeSystem);
|
||||
});
|
||||
|
||||
it("Disallows setting of time system without bounds", function () {
|
||||
expect(tc.timeSystem()).not.toBe(timeSystem);
|
||||
expect(tc.timeSystem.bind(tc, timeSystem)).toThrow();
|
||||
expect(tc.timeSystem()).not.toBe(timeSystem);
|
||||
});
|
||||
|
||||
it("Emits an event when time system changes", function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
tc.on("timeSystem", eventListener);
|
||||
tc.timeSystem(timeSystem, bounds);
|
||||
expect(eventListener).toHaveBeenCalledWith(timeSystem);
|
||||
});
|
||||
|
||||
it("Emits an event when time of interest changes", function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
tc.on("timeOfInterest", eventListener);
|
||||
tc.timeOfInterest(toi);
|
||||
expect(eventListener).toHaveBeenCalledWith(toi);
|
||||
});
|
||||
|
||||
it("Emits an event when bounds change", function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
tc.on("bounds", eventListener);
|
||||
tc.bounds(bounds);
|
||||
expect(eventListener).toHaveBeenCalledWith(bounds);
|
||||
});
|
||||
|
||||
it("Emits an event when follow mode changes", function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
tc.on("follow", eventListener);
|
||||
tc.follow(follow);
|
||||
expect(eventListener).toHaveBeenCalledWith(follow);
|
||||
});
|
||||
});
|
||||
});
|
@ -73,8 +73,22 @@ define([], function () {
|
||||
throw new Error('Not implemented');
|
||||
};
|
||||
|
||||
/**
|
||||
/***
|
||||
*
|
||||
* @typedef {object} TimeConductorZoom
|
||||
* @property {number} min The largest time span that the time
|
||||
* conductor can display in this time system. ie. the span of the time
|
||||
* conductor in its most zoomed out state.
|
||||
* @property {number} max The smallest time span that the time
|
||||
* conductor can display in this time system. ie. the span of the time
|
||||
* conductor bounds in its most zoomed in state.
|
||||
*
|
||||
* @typedef {object} TimeSystemDefault
|
||||
* @property {TimeConductorDeltas} deltas The deltas to apply by default
|
||||
* when this time system is active. Applies to real-time modes only
|
||||
* @property {TimeConductorBounds} bounds The bounds to apply by default
|
||||
* when this time system is active
|
||||
* @property {TimeConductorZoom} zoom Default min and max zoom levels
|
||||
* @returns {TimeSystemDefault[]} At least one set of default values for
|
||||
* this time system.
|
||||
*/
|
||||
|
@ -0,0 +1,240 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[
|
||||
"d3"
|
||||
],
|
||||
function (d3) {
|
||||
var PADDING = 1;
|
||||
|
||||
/**
|
||||
* Controller that renders a horizontal time scale spanning the current bounds defined in the time conductor.
|
||||
* Used by the mct-conductor-axis directive
|
||||
* @constructor
|
||||
*/
|
||||
function ConductorAxisController(openmct, formatService, conductorViewService, scope, element) {
|
||||
// Dependencies
|
||||
this.formatService = formatService;
|
||||
this.conductor = openmct.conductor;
|
||||
this.conductorViewService = conductorViewService;
|
||||
|
||||
this.scope = scope;
|
||||
this.initialized = false;
|
||||
|
||||
this.bounds = this.conductor.bounds();
|
||||
this.timeSystem = this.conductor.timeSystem();
|
||||
|
||||
//Bind all class functions to 'this'
|
||||
Object.keys(ConductorAxisController.prototype).filter(function (key) {
|
||||
return typeof ConductorAxisController.prototype[key] === 'function';
|
||||
}).forEach(function (key) {
|
||||
this[key] = ConductorAxisController.prototype[key].bind(this);
|
||||
}.bind(this));
|
||||
|
||||
this.initialize(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ConductorAxisController.prototype.destroy = function () {
|
||||
this.conductor.off('timeSystem', this.changeTimeSystem);
|
||||
this.conductor.off('bounds', this.changeBounds);
|
||||
this.conductorViewService.off("zoom", this.onZoom);
|
||||
this.conductorViewService.off("zoom-stop", this.onZoomStop);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ConductorAxisController.prototype.initialize = function (element) {
|
||||
this.target = element[0].firstChild;
|
||||
var height = this.target.offsetHeight;
|
||||
var vis = d3.select(this.target)
|
||||
.append("svg:svg")
|
||||
.attr("width", "100%")
|
||||
.attr("height", height);
|
||||
|
||||
this.xAxis = d3.axisTop();
|
||||
|
||||
// draw x axis with labels and move to the bottom of the chart area
|
||||
this.axisElement = vis.append("g")
|
||||
.attr("transform", "translate(0," + (height - PADDING) + ")");
|
||||
|
||||
if (this.timeSystem !== undefined) {
|
||||
this.changeTimeSystem(this.timeSystem);
|
||||
this.setScale();
|
||||
}
|
||||
|
||||
//Respond to changes in conductor
|
||||
this.conductor.on("timeSystem", this.changeTimeSystem);
|
||||
this.conductor.on("bounds", this.changeBounds);
|
||||
|
||||
this.scope.$on("$destroy", this.destroy);
|
||||
|
||||
this.conductorViewService.on("zoom", this.onZoom);
|
||||
this.conductorViewService.on("zoom-stop", this.onZoomStop);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ConductorAxisController.prototype.changeBounds = function (bounds) {
|
||||
this.bounds = bounds;
|
||||
if (!this.zooming) {
|
||||
this.setScale();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the scale of the axis, based on current conductor bounds.
|
||||
*/
|
||||
ConductorAxisController.prototype.setScale = function () {
|
||||
var width = this.target.offsetWidth;
|
||||
var timeSystem = this.conductor.timeSystem();
|
||||
var bounds = this.bounds;
|
||||
|
||||
if (timeSystem.isUTCBased()) {
|
||||
this.xScale = this.xScale || d3.scaleUtc();
|
||||
this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
|
||||
} else {
|
||||
this.xScale = this.xScale || d3.scaleLinear();
|
||||
this.xScale.domain([bounds.start, bounds.end]);
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
|
||||
this.xScale.range([PADDING, width - PADDING * 2]);
|
||||
this.axisElement.call(this.xAxis);
|
||||
|
||||
this.msPerPixel = (bounds.end - bounds.start) / width;
|
||||
};
|
||||
|
||||
/**
|
||||
* When the time system changes, update the scale and formatter used for showing times.
|
||||
* @param timeSystem
|
||||
*/
|
||||
ConductorAxisController.prototype.changeTimeSystem = function (timeSystem) {
|
||||
this.timeSystem = timeSystem;
|
||||
|
||||
var key = timeSystem.formats()[0];
|
||||
if (key !== undefined) {
|
||||
var format = this.formatService.getFormat(key);
|
||||
var bounds = this.conductor.bounds();
|
||||
|
||||
//The D3 scale used depends on the type of time system as d3
|
||||
// supports UTC out of the box.
|
||||
if (timeSystem.isUTCBased()) {
|
||||
this.xScale = d3.scaleUtc();
|
||||
} else {
|
||||
this.xScale = d3.scaleLinear();
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
|
||||
//Define a custom format function
|
||||
this.xAxis.tickFormat(function (tickValue) {
|
||||
// Normalize date representations to numbers
|
||||
if (tickValue instanceof Date) {
|
||||
tickValue = tickValue.getTime();
|
||||
}
|
||||
return format.format(tickValue, {
|
||||
min: bounds.start,
|
||||
max: bounds.end
|
||||
});
|
||||
});
|
||||
this.axisElement.call(this.xAxis);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The user has stopped panning the time conductor scale element.
|
||||
* @event panStop
|
||||
*/
|
||||
/**
|
||||
* Called on release of mouse button after dragging the scale left or right.
|
||||
* @fires platform.features.conductor.ConductorAxisController~panStop
|
||||
*/
|
||||
ConductorAxisController.prototype.panStop = function () {
|
||||
//resync view bounds with time conductor bounds
|
||||
this.conductorViewService.emit("pan-stop");
|
||||
this.conductor.bounds(this.bounds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rescales the axis when the user zooms. Although zoom ultimately results in a bounds change once the user
|
||||
* releases the zoom slider, dragging the slider will not immediately change the conductor bounds. It will
|
||||
* however immediately update the scale and the bounds displayed in the UI.
|
||||
* @private
|
||||
* @param {ZoomLevel}
|
||||
*/
|
||||
ConductorAxisController.prototype.onZoom = function (zoom) {
|
||||
this.zooming = true;
|
||||
|
||||
this.bounds = zoom.bounds;
|
||||
this.setScale();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ConductorAxisController.prototype.onZoomStop = function (zoom) {
|
||||
this.zooming = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @event platform.features.conductor.ConductorAxisController~pan
|
||||
* Fired when the time conductor is panned
|
||||
*/
|
||||
/**
|
||||
* Initiate panning via a click + drag gesture on the time conductor
|
||||
* scale. Panning triggers a "pan" event
|
||||
* @param {number} delta the offset from the original click event
|
||||
* @see TimeConductorViewService#
|
||||
* @fires platform.features.conductor.ConductorAxisController~pan
|
||||
*/
|
||||
ConductorAxisController.prototype.pan = function (delta) {
|
||||
if (!this.conductor.follow()) {
|
||||
var deltaInMs = delta[0] * this.msPerPixel;
|
||||
var bounds = this.conductor.bounds();
|
||||
var start = Math.floor((bounds.start - deltaInMs) / 1000) * 1000;
|
||||
var end = Math.floor((bounds.end - deltaInMs) / 1000) * 1000;
|
||||
this.bounds = {
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
this.setScale();
|
||||
this.conductorViewService.emit("pan", this.bounds);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked on element resize. Will rebuild the scale based on the new dimensions of the element.
|
||||
*/
|
||||
ConductorAxisController.prototype.resize = function () {
|
||||
this.setScale();
|
||||
};
|
||||
|
||||
return ConductorAxisController;
|
||||
}
|
||||
);
|
@ -20,16 +20,33 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./MctConductorAxis'], function (MctConductorAxis) {
|
||||
describe("The MctConductorAxis directive", function () {
|
||||
var directive,
|
||||
define([
|
||||
'./ConductorAxisController',
|
||||
'zepto',
|
||||
'd3'
|
||||
], function (
|
||||
ConductorAxisController,
|
||||
$,
|
||||
d3
|
||||
) {
|
||||
describe("The ConductorAxisController", function () {
|
||||
var controller,
|
||||
mockConductor,
|
||||
mockConductorViewService,
|
||||
mockFormatService,
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockTarget,
|
||||
mockBounds,
|
||||
d3;
|
||||
element,
|
||||
mockTimeSystem,
|
||||
mockFormat;
|
||||
|
||||
function getCallback(target, name) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === name;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj("scope", [
|
||||
@ -52,7 +69,8 @@ define(['./MctConductorAxis'], function (MctConductorAxis) {
|
||||
"timeSystem",
|
||||
"bounds",
|
||||
"on",
|
||||
"off"
|
||||
"off",
|
||||
"follow"
|
||||
]);
|
||||
mockConductor.bounds.andReturn(mockBounds);
|
||||
|
||||
@ -60,87 +78,101 @@ define(['./MctConductorAxis'], function (MctConductorAxis) {
|
||||
"getFormat"
|
||||
]);
|
||||
|
||||
var d3Functions = [
|
||||
"scale",
|
||||
"scaleUtc",
|
||||
"scaleLinear",
|
||||
"select",
|
||||
"append",
|
||||
"attr",
|
||||
"axisTop",
|
||||
"call",
|
||||
"tickFormat",
|
||||
"domain",
|
||||
"range"
|
||||
];
|
||||
d3 = jasmine.createSpyObj("d3", d3Functions);
|
||||
d3Functions.forEach(function (func) {
|
||||
d3[func].andReturn(d3);
|
||||
});
|
||||
mockConductorViewService = jasmine.createSpyObj("conductorViewService", [
|
||||
"on",
|
||||
"off",
|
||||
"emit"
|
||||
]);
|
||||
|
||||
directive = new MctConductorAxis(mockConductor, mockFormatService);
|
||||
directive.d3 = d3;
|
||||
directive.link(mockScope, [mockElement]);
|
||||
spyOn(d3, 'scaleUtc').andCallThrough();
|
||||
spyOn(d3, 'scaleLinear').andCallThrough();
|
||||
|
||||
element = $('<div style="width: 100px;"><div style="width: 100%;"></div></div>');
|
||||
$(document).find('body').append(element);
|
||||
controller = new ConductorAxisController({conductor: mockConductor}, mockFormatService, mockConductorViewService, mockScope, element);
|
||||
|
||||
mockTimeSystem = jasmine.createSpyObj("timeSystem", [
|
||||
"formats",
|
||||
"isUTCBased"
|
||||
]);
|
||||
mockFormat = jasmine.createSpyObj("format", [
|
||||
"format"
|
||||
]);
|
||||
|
||||
mockTimeSystem.formats.andReturn(["mockFormat"]);
|
||||
mockFormatService.getFormat.andReturn(mockFormat);
|
||||
mockConductor.timeSystem.andReturn(mockTimeSystem);
|
||||
mockTimeSystem.isUTCBased.andReturn(false);
|
||||
});
|
||||
|
||||
it("listens for changes to time system and bounds", function () {
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem);
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("bounds", directive.setScale);
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("bounds", controller.changeBounds);
|
||||
});
|
||||
|
||||
it("on scope destruction, deregisters listeners", function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", directive.destroy);
|
||||
directive.destroy();
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("bounds", directive.setScale);
|
||||
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", controller.destroy);
|
||||
controller.destroy();
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("bounds", controller.changeBounds);
|
||||
});
|
||||
|
||||
describe("when the time system changes", function () {
|
||||
var mockTimeSystem;
|
||||
var mockFormat;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeSystem = jasmine.createSpyObj("timeSystem", [
|
||||
"formats",
|
||||
"isUTCBased"
|
||||
]);
|
||||
mockFormat = jasmine.createSpyObj("format", [
|
||||
"format"
|
||||
]);
|
||||
|
||||
mockTimeSystem.formats.andReturn(["mockFormat"]);
|
||||
mockFormatService.getFormat.andReturn(mockFormat);
|
||||
});
|
||||
|
||||
it("uses a UTC scale for UTC time systems", function () {
|
||||
mockTimeSystem.isUTCBased.andReturn(true);
|
||||
directive.changeTimeSystem(mockTimeSystem);
|
||||
controller.changeTimeSystem(mockTimeSystem);
|
||||
|
||||
expect(d3.scaleUtc).toHaveBeenCalled();
|
||||
expect(d3.scaleLinear).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses a linear scale for non-UTC time systems", function () {
|
||||
mockTimeSystem.isUTCBased.andReturn(false);
|
||||
directive.changeTimeSystem(mockTimeSystem);
|
||||
controller.changeTimeSystem(mockTimeSystem);
|
||||
expect(d3.scaleLinear).toHaveBeenCalled();
|
||||
expect(d3.scaleUtc).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets axis domain to time conductor bounds", function () {
|
||||
mockTimeSystem.isUTCBased.andReturn(false);
|
||||
mockConductor.timeSystem.andReturn(mockTimeSystem);
|
||||
|
||||
directive.setScale();
|
||||
expect(d3.domain).toHaveBeenCalledWith([mockBounds.start, mockBounds.end]);
|
||||
controller.setScale();
|
||||
expect(controller.xScale.domain()).toEqual([mockBounds.start, mockBounds.end]);
|
||||
});
|
||||
|
||||
it("uses the format specified by the time system to format tick" +
|
||||
" labels", function () {
|
||||
directive.changeTimeSystem(mockTimeSystem);
|
||||
expect(d3.tickFormat).toHaveBeenCalled();
|
||||
d3.tickFormat.mostRecentCall.args[0]();
|
||||
controller.changeTimeSystem(mockTimeSystem);
|
||||
expect(mockFormat.format).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('responds to zoom events', function () {
|
||||
expect(mockConductorViewService.on).toHaveBeenCalledWith("zoom", controller.onZoom);
|
||||
var cb = getCallback(mockConductorViewService.on, "zoom");
|
||||
spyOn(controller, 'setScale').andCallThrough();
|
||||
cb({bounds: {start: 0, end: 100}});
|
||||
expect(controller.setScale).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('adjusts scale on pan', function () {
|
||||
spyOn(controller, 'setScale').andCallThrough();
|
||||
controller.pan(100);
|
||||
expect(controller.setScale).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('emits event on pan', function () {
|
||||
spyOn(controller, 'setScale').andCallThrough();
|
||||
controller.pan(100);
|
||||
expect(mockConductorViewService.emit).toHaveBeenCalledWith("pan", jasmine.any(Object));
|
||||
});
|
||||
|
||||
it('cleans up listeners on destruction', function () {
|
||||
controller.destroy();
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("bounds", controller.changeBounds);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
|
||||
|
||||
expect(mockConductorViewService.off).toHaveBeenCalledWith("zoom", controller.onZoom);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,124 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["zepto"],
|
||||
function ($) {
|
||||
|
||||
/**
|
||||
* Controller for the Time of Interest indicator in the conductor itself. Sets the horizontal position of the
|
||||
* TOI indicator based on the current value of the TOI, and the width of the TOI conductor.
|
||||
* @memberof platform.features.conductor
|
||||
*/
|
||||
function ConductorTOIController($scope, openmct, conductorViewService) {
|
||||
this.conductor = openmct.conductor;
|
||||
this.conductorViewService = conductorViewService;
|
||||
|
||||
//Bind all class functions to 'this'
|
||||
Object.keys(ConductorTOIController.prototype).filter(function (key) {
|
||||
return typeof ConductorTOIController.prototype[key] === 'function';
|
||||
}).forEach(function (key) {
|
||||
this[key] = ConductorTOIController.prototype[key].bind(this);
|
||||
}.bind(this));
|
||||
|
||||
this.conductor.on('timeOfInterest', this.changeTimeOfInterest);
|
||||
this.conductorViewService.on('zoom', this.setOffsetFromZoom);
|
||||
this.conductorViewService.on('pan', this.setOffsetFromBounds);
|
||||
|
||||
var timeOfInterest = this.conductor.timeOfInterest();
|
||||
if (timeOfInterest) {
|
||||
this.changeTimeOfInterest(timeOfInterest);
|
||||
}
|
||||
|
||||
$scope.$on('$destroy', this.destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ConductorTOIController.prototype.destroy = function () {
|
||||
this.conductor.off('timeOfInterest', this.changeTimeOfInterest);
|
||||
this.conductorViewService.off('zoom', this.setOffsetFromZoom);
|
||||
this.conductorViewService.off('pan', this.setOffsetFromBounds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given some bounds, set horizontal position of TOI indicator based
|
||||
* on current conductor TOI value. Bounds are provided so that
|
||||
* ephemeral bounds from zoom and pan events can be used as well
|
||||
* as current conductor bounds, allowing TOI to be updated in
|
||||
* realtime during scroll and zoom.
|
||||
* @param {TimeConductorBounds} bounds
|
||||
*/
|
||||
ConductorTOIController.prototype.setOffsetFromBounds = function (bounds) {
|
||||
var toi = this.conductor.timeOfInterest();
|
||||
if (toi !== undefined) {
|
||||
var offset = toi - bounds.start;
|
||||
var duration = bounds.end - bounds.start;
|
||||
this.left = offset / duration * 100;
|
||||
this.pinned = true;
|
||||
} else {
|
||||
this.left = 0;
|
||||
this.pinned = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ConductorTOIController.prototype.setOffsetFromZoom = function (zoom) {
|
||||
return this.setOffsetFromBounds(zoom.bounds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked when time of interest changes. Will set the horizontal offset of the TOI indicator.
|
||||
* @private
|
||||
*/
|
||||
ConductorTOIController.prototype.changeTimeOfInterest = function () {
|
||||
var bounds = this.conductor.bounds();
|
||||
if (bounds) {
|
||||
this.setOffsetFromBounds(bounds);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* On a mouse click event within the TOI element, convert position within element to a time of interest, and
|
||||
* set the time of interest on the conductor.
|
||||
* @param e The angular $event object
|
||||
*/
|
||||
ConductorTOIController.prototype.setTOIFromPosition = function (e) {
|
||||
//TOI is set using the alt key modified + primary click
|
||||
if (e.altKey) {
|
||||
var element = $(e.currentTarget);
|
||||
var width = element.width();
|
||||
var relativeX = e.pageX - element.offset().left;
|
||||
var percX = relativeX / width;
|
||||
var bounds = this.conductor.bounds();
|
||||
var timeRange = bounds.end - bounds.start;
|
||||
|
||||
this.conductor.timeOfInterest(timeRange * percX + bounds.start);
|
||||
}
|
||||
};
|
||||
|
||||
return ConductorTOIController;
|
||||
}
|
||||
);
|
@ -0,0 +1,153 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./ConductorTOIController'
|
||||
], function (
|
||||
ConductorTOIController
|
||||
) {
|
||||
var mockConductor;
|
||||
var mockConductorViewService;
|
||||
var mockScope;
|
||||
var mockAPI;
|
||||
var conductorTOIController;
|
||||
|
||||
function getNamedCallback(thing, name) {
|
||||
return thing.calls.filter(function (call) {
|
||||
return call.args[0] === name;
|
||||
}).map(function (call) {
|
||||
return call.args;
|
||||
})[0][1];
|
||||
}
|
||||
|
||||
describe("The ConductorTOIController", function () {
|
||||
beforeEach(function () {
|
||||
mockConductor = jasmine.createSpyObj("conductor", [
|
||||
"bounds",
|
||||
"timeOfInterest",
|
||||
"on",
|
||||
"off"
|
||||
]);
|
||||
mockAPI = {conductor: mockConductor};
|
||||
|
||||
mockConductorViewService = jasmine.createSpyObj("conductorViewService", [
|
||||
"on",
|
||||
"off"
|
||||
]);
|
||||
|
||||
mockScope = jasmine.createSpyObj("openMCT", [
|
||||
"$on"
|
||||
]);
|
||||
|
||||
conductorTOIController = new ConductorTOIController(mockScope, mockAPI, mockConductorViewService);
|
||||
});
|
||||
|
||||
it("listens to changes in the time of interest on the conductor", function () {
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("timeOfInterest", jasmine.any(Function));
|
||||
});
|
||||
|
||||
describe("when responding to changes in the time of interest", function () {
|
||||
var toiCallback;
|
||||
beforeEach(function () {
|
||||
var bounds = {
|
||||
start: 0,
|
||||
end: 200
|
||||
};
|
||||
mockConductor.bounds.andReturn(bounds);
|
||||
toiCallback = getNamedCallback(mockConductor.on, "timeOfInterest");
|
||||
});
|
||||
|
||||
it("calculates the correct horizontal offset based on bounds and current TOI", function () {
|
||||
//Expect time of interest position to be 50% of element width
|
||||
mockConductor.timeOfInterest.andReturn(100);
|
||||
toiCallback();
|
||||
expect(conductorTOIController.left).toBe(50);
|
||||
|
||||
//Expect time of interest position to be 25% of element width
|
||||
mockConductor.timeOfInterest.andReturn(50);
|
||||
toiCallback();
|
||||
expect(conductorTOIController.left).toBe(25);
|
||||
|
||||
//Expect time of interest position to be 0% of element width
|
||||
mockConductor.timeOfInterest.andReturn(0);
|
||||
toiCallback();
|
||||
expect(conductorTOIController.left).toBe(0);
|
||||
|
||||
//Expect time of interest position to be 100% of element width
|
||||
mockConductor.timeOfInterest.andReturn(200);
|
||||
toiCallback();
|
||||
expect(conductorTOIController.left).toBe(100);
|
||||
});
|
||||
|
||||
it("renders the TOI indicator visible", function () {
|
||||
expect(conductorTOIController.pinned).toBeFalsy();
|
||||
mockConductor.timeOfInterest.andReturn(100);
|
||||
toiCallback();
|
||||
expect(conductorTOIController.pinned).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("responds to zoom events", function () {
|
||||
var mockZoom = {
|
||||
bounds: {
|
||||
start: 500,
|
||||
end: 1000
|
||||
}
|
||||
};
|
||||
expect(mockConductorViewService.on).toHaveBeenCalledWith("zoom", jasmine.any(Function));
|
||||
|
||||
// Should correspond to horizontal offset of 50%
|
||||
mockConductor.timeOfInterest.andReturn(750);
|
||||
var zoomCallback = getNamedCallback(mockConductorViewService.on, "zoom");
|
||||
zoomCallback(mockZoom);
|
||||
expect(conductorTOIController.left).toBe(50);
|
||||
});
|
||||
|
||||
it("responds to pan events", function () {
|
||||
var mockPanBounds = {
|
||||
start: 1000,
|
||||
end: 3000
|
||||
};
|
||||
|
||||
expect(mockConductorViewService.on).toHaveBeenCalledWith("pan", jasmine.any(Function));
|
||||
|
||||
// Should correspond to horizontal offset of 25%
|
||||
mockConductor.timeOfInterest.andReturn(1500);
|
||||
var panCallback = getNamedCallback(mockConductorViewService.on, "pan");
|
||||
panCallback(mockPanBounds);
|
||||
expect(conductorTOIController.left).toBe(25);
|
||||
});
|
||||
|
||||
|
||||
it("Cleans up all listeners when controller destroyed", function () {
|
||||
var zoomCB = getNamedCallback(mockConductorViewService.on, "zoom");
|
||||
var panCB = getNamedCallback(mockConductorViewService.on, "pan");
|
||||
var toiCB = getNamedCallback(mockConductor.on, "timeOfInterest");
|
||||
|
||||
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", jasmine.any(Function));
|
||||
getNamedCallback(mockScope.$on, "$destroy")();
|
||||
expect(mockConductorViewService.off).toHaveBeenCalledWith("zoom", zoomCB);
|
||||
expect(mockConductorViewService.off).toHaveBeenCalledWith("pan", panCB);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("timeOfInterest", toiCB);
|
||||
});
|
||||
});
|
||||
});
|
@ -20,127 +20,35 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[
|
||||
"d3"
|
||||
],
|
||||
function (d3) {
|
||||
var PADDING = 1;
|
||||
|
||||
define(['./ConductorAxisController'], function (ConductorAxisController) {
|
||||
function MctConductorAxis() {
|
||||
/**
|
||||
* The mct-conductor-axis renders a horizontal axis with regular
|
||||
* labelled 'ticks'. It requires 'start' and 'end' integer values to
|
||||
* be specified as attributes.
|
||||
*/
|
||||
function MCTConductorAxis(conductor, formatService) {
|
||||
// Dependencies
|
||||
this.d3 = d3;
|
||||
this.conductor = conductor;
|
||||
this.formatService = formatService;
|
||||
|
||||
// Runtime properties (set by 'link' function)
|
||||
this.target = undefined;
|
||||
this.xScale = undefined;
|
||||
this.xAxis = undefined;
|
||||
this.axisElement = undefined;
|
||||
return {
|
||||
controller: [
|
||||
'openmct',
|
||||
'formatService',
|
||||
'timeConductorViewService',
|
||||
'$scope',
|
||||
'$element',
|
||||
ConductorAxisController
|
||||
],
|
||||
controllerAs: 'axis',
|
||||
|
||||
// Angular Directive interface
|
||||
this.link = this.link.bind(this);
|
||||
this.restrict = "E";
|
||||
this.template =
|
||||
"<div class=\"l-axis-holder\" mct-resize=\"resize()\"></div>";
|
||||
this.priority = 1000;
|
||||
restrict: 'E',
|
||||
priority: 1000,
|
||||
|
||||
//Bind all class functions to 'this'
|
||||
Object.keys(MCTConductorAxis.prototype).filter(function (key) {
|
||||
return typeof MCTConductorAxis.prototype[key] === 'function';
|
||||
}).forEach(function (key) {
|
||||
this[key] = this[key].bind(this);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
MCTConductorAxis.prototype.setScale = function () {
|
||||
var width = this.target.offsetWidth;
|
||||
var timeSystem = this.conductor.timeSystem();
|
||||
var bounds = this.conductor.bounds();
|
||||
|
||||
if (timeSystem.isUTCBased()) {
|
||||
this.xScale = this.xScale || this.d3.scaleUtc();
|
||||
this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
|
||||
} else {
|
||||
this.xScale = this.xScale || this.d3.scaleLinear();
|
||||
this.xScale.domain([bounds.start, bounds.end]);
|
||||
}
|
||||
|
||||
this.xScale.range([PADDING, width - PADDING * 2]);
|
||||
this.axisElement.call(this.xAxis);
|
||||
};
|
||||
|
||||
MCTConductorAxis.prototype.changeTimeSystem = function (timeSystem) {
|
||||
var key = timeSystem.formats()[0];
|
||||
if (key !== undefined) {
|
||||
var format = this.formatService.getFormat(key);
|
||||
var bounds = this.conductor.bounds();
|
||||
|
||||
if (timeSystem.isUTCBased()) {
|
||||
this.xScale = this.d3.scaleUtc();
|
||||
} else {
|
||||
this.xScale = this.d3.scaleLinear();
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
//Define a custom format function
|
||||
this.xAxis.tickFormat(function (tickValue) {
|
||||
// Normalize date representations to numbers
|
||||
if (tickValue instanceof Date) {
|
||||
tickValue = tickValue.getTime();
|
||||
}
|
||||
return format.format(tickValue, {
|
||||
min: bounds.start,
|
||||
max: bounds.end
|
||||
});
|
||||
});
|
||||
this.axisElement.call(this.xAxis);
|
||||
}
|
||||
};
|
||||
|
||||
MCTConductorAxis.prototype.destroy = function () {
|
||||
this.conductor.off('timeSystem', this.changeTimeSystem);
|
||||
this.conductor.off('bounds', this.setScale);
|
||||
};
|
||||
|
||||
MCTConductorAxis.prototype.link = function (scope, element) {
|
||||
var conductor = this.conductor;
|
||||
this.target = element[0].firstChild;
|
||||
var height = this.target.offsetHeight;
|
||||
var vis = this.d3.select(this.target)
|
||||
.append('svg:svg')
|
||||
.attr('width', '100%')
|
||||
.attr('height', height);
|
||||
|
||||
this.xAxis = this.d3.axisTop();
|
||||
|
||||
// draw x axis with labels and move to the bottom of the chart area
|
||||
this.axisElement = vis.append("g")
|
||||
.attr("transform", "translate(0," + (height - PADDING) + ")");
|
||||
|
||||
scope.resize = this.setScale;
|
||||
|
||||
conductor.on('timeSystem', this.changeTimeSystem);
|
||||
|
||||
//On conductor bounds changes, redraw ticks
|
||||
conductor.on('bounds', this.setScale);
|
||||
|
||||
scope.$on("$destroy", this.destroy);
|
||||
|
||||
if (conductor.timeSystem() !== undefined) {
|
||||
this.changeTimeSystem(conductor.timeSystem());
|
||||
this.setScale();
|
||||
}
|
||||
};
|
||||
|
||||
return function (conductor, formatService) {
|
||||
return new MCTConductorAxis(conductor, formatService);
|
||||
template: '<div class="l-axis-holder" ' +
|
||||
' mct-drag-down="axis.panStart()"' +
|
||||
' mct-drag-up="axis.panStop(delta)"' +
|
||||
' mct-drag="axis.pan(delta)"' +
|
||||
' mct-resize="axis.resize()"></div>'
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return MctConductorAxis;
|
||||
});
|
||||
|
@ -26,7 +26,13 @@ define(
|
||||
],
|
||||
function (TimeConductorValidation) {
|
||||
|
||||
function TimeConductorController($scope, $window, timeConductor, conductorViewService, timeSystems) {
|
||||
/**
|
||||
* Controller for the Time Conductor UI element. The Time Conductor includes form fields for specifying time
|
||||
* bounds and relative time deltas for queries, as well as controls for selection mode, time systems, and zooming.
|
||||
* @memberof platform.features.conductor
|
||||
* @constructor
|
||||
*/
|
||||
function TimeConductorController($scope, $window, openmct, conductorViewService, timeSystems, formatService) {
|
||||
|
||||
var self = this;
|
||||
|
||||
@ -40,9 +46,10 @@ define(
|
||||
this.$scope = $scope;
|
||||
this.$window = $window;
|
||||
this.conductorViewService = conductorViewService;
|
||||
this.conductor = timeConductor;
|
||||
this.conductor = openmct.conductor;
|
||||
this.modes = conductorViewService.availableModes();
|
||||
this.validation = new TimeConductorValidation(this.conductor);
|
||||
this.formatService = formatService;
|
||||
|
||||
// Construct the provided time system definitions
|
||||
this.timeSystems = timeSystems.map(function (timeSystemConstructor) {
|
||||
@ -52,7 +59,7 @@ define(
|
||||
//Set the initial state of the view based on current time conductor
|
||||
this.initializeScope();
|
||||
|
||||
this.conductor.on('bounds', this.setFormFromBounds);
|
||||
this.conductor.on('bounds', this.changeBounds);
|
||||
this.conductor.on('timeSystem', this.changeTimeSystem);
|
||||
|
||||
// If no mode selected, select fixed as the default
|
||||
@ -71,8 +78,9 @@ define(
|
||||
//If conductor has a time system selected already, populate the
|
||||
//form from it
|
||||
this.$scope.timeSystemModel = {};
|
||||
if (this.conductor.timeSystem()) {
|
||||
this.setFormFromTimeSystem(this.conductor.timeSystem());
|
||||
var timeSystem = this.conductor.timeSystem();
|
||||
if (timeSystem) {
|
||||
this.setFormFromTimeSystem(timeSystem);
|
||||
}
|
||||
|
||||
//Represents the various modes, and the currently selected mode
|
||||
@ -98,34 +106,80 @@ define(
|
||||
// Watch scope for selection of mode or time system by user
|
||||
this.$scope.$watch('modeModel.selectedKey', this.setMode);
|
||||
|
||||
this.conductorViewService.on('pan', this.onPan);
|
||||
this.conductorViewService.on('pan-stop', this.onPanStop);
|
||||
|
||||
this.$scope.$on('$destroy', this.destroy);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TimeConductorController.prototype.destroy = function () {
|
||||
this.conductor.off('bounds', this.setFormFromBounds);
|
||||
this.conductor.off('bounds', this.changeBounds);
|
||||
this.conductor.off('timeSystem', this.changeTimeSystem);
|
||||
|
||||
this.conductorViewService.off('pan', this.onPan);
|
||||
this.conductorViewService.off('pan-stop', this.onPanStop);
|
||||
};
|
||||
|
||||
/**
|
||||
* When the conductor bounds change, set the bounds in the form.
|
||||
* @private
|
||||
* @param {TimeConductorBounds} bounds
|
||||
*/
|
||||
TimeConductorController.prototype.changeBounds = function (bounds) {
|
||||
//If a zoom or pan is currently in progress, do not override form values.
|
||||
if (!this.zooming && !this.panning) {
|
||||
this.setFormFromBounds(bounds);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Does the currently selected time system support zooming? To
|
||||
* support zooming a time system must, at a minimum, define some
|
||||
* values for maximum and minimum zoom levels. Additionally
|
||||
* TimeFormats, a related concept, may also support providing time
|
||||
* unit feedback for the zoom level label, eg "seconds, minutes,
|
||||
* hours, etc..."
|
||||
* @returns {boolean}
|
||||
*/
|
||||
TimeConductorController.prototype.supportsZoom = function () {
|
||||
var timeSystem = this.conductor.timeSystem();
|
||||
return timeSystem &&
|
||||
timeSystem.defaults() &&
|
||||
timeSystem.defaults().zoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the bounds change in the time conductor. Synchronizes
|
||||
* the bounds values in the time conductor with those in the form
|
||||
*
|
||||
* @private
|
||||
* @param {TimeConductorBounds}
|
||||
*/
|
||||
TimeConductorController.prototype.setFormFromBounds = function (bounds) {
|
||||
this.$scope.boundsModel.start = bounds.start;
|
||||
this.$scope.boundsModel.end = bounds.end;
|
||||
if (!this.pendingUpdate) {
|
||||
this.pendingUpdate = true;
|
||||
this.$window.requestAnimationFrame(function () {
|
||||
this.pendingUpdate = false;
|
||||
this.$scope.$digest();
|
||||
}.bind(this));
|
||||
if (!this.zooming && !this.panning) {
|
||||
this.$scope.boundsModel.start = bounds.start;
|
||||
this.$scope.boundsModel.end = bounds.end;
|
||||
|
||||
if (this.supportsZoom()) {
|
||||
this.currentZoom = this.toSliderValue(bounds.end - bounds.start);
|
||||
this.toTimeUnits(bounds.end - bounds.start);
|
||||
}
|
||||
|
||||
if (!this.pendingUpdate) {
|
||||
this.pendingUpdate = true;
|
||||
this.$window.requestAnimationFrame(function () {
|
||||
this.pendingUpdate = false;
|
||||
this.$scope.$digest();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* On mode change, populate form based on time systems available
|
||||
* from the selected mode.
|
||||
* @param mode
|
||||
*/
|
||||
TimeConductorController.prototype.setFormFromMode = function (mode) {
|
||||
this.$scope.modeModel.selectedKey = mode;
|
||||
@ -138,6 +192,7 @@ define(
|
||||
};
|
||||
|
||||
/**
|
||||
* When the deltas change, update the values in the UI
|
||||
* @private
|
||||
*/
|
||||
TimeConductorController.prototype.setFormFromDeltas = function (deltas) {
|
||||
@ -146,14 +201,20 @@ define(
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Initialize the form when time system changes.
|
||||
* @param {TimeSystem} timeSystem
|
||||
*/
|
||||
TimeConductorController.prototype.setFormFromTimeSystem = function (timeSystem) {
|
||||
this.$scope.timeSystemModel.selected = timeSystem;
|
||||
this.$scope.timeSystemModel.format = timeSystem.formats()[0];
|
||||
this.$scope.timeSystemModel.deltaFormat = timeSystem.deltaFormat();
|
||||
};
|
||||
var timeSystemModel = this.$scope.timeSystemModel;
|
||||
timeSystemModel.selected = timeSystem;
|
||||
timeSystemModel.format = timeSystem.formats()[0];
|
||||
timeSystemModel.deltaFormat = timeSystem.deltaFormat();
|
||||
|
||||
if (this.supportsZoom()) {
|
||||
timeSystemModel.minZoom = timeSystem.defaults().zoom.min;
|
||||
timeSystemModel.maxZoom = timeSystem.defaults().zoom.max;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when form values are changed. Synchronizes the form with
|
||||
@ -222,11 +283,11 @@ define(
|
||||
* Sets the selected time system. Will populate form with the default
|
||||
* bounds and deltas defined in the selected time system.
|
||||
*
|
||||
* @private
|
||||
* @param newTimeSystem
|
||||
*/
|
||||
TimeConductorController.prototype.changeTimeSystem = function (newTimeSystem) {
|
||||
if (newTimeSystem && (newTimeSystem !== this.$scope.timeSystemModel.selected)) {
|
||||
this.setFormFromTimeSystem(newTimeSystem);
|
||||
if (newTimeSystem.defaults()) {
|
||||
var deltas = newTimeSystem.defaults().deltas || {start: 0, end: 0};
|
||||
var bounds = newTimeSystem.defaults().bounds || {start: 0, end: 0};
|
||||
@ -234,10 +295,96 @@ define(
|
||||
this.setFormFromDeltas(deltas);
|
||||
this.setFormFromBounds(bounds);
|
||||
}
|
||||
this.setFormFromTimeSystem(newTimeSystem);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a time span and calculates a slider increment value, used
|
||||
* to set the horizontal offset of the slider.
|
||||
* @param {number} timeSpan a duration of time, in ms
|
||||
* @returns {number} a value between 0.01 and 0.99, in increments of .01
|
||||
*/
|
||||
TimeConductorController.prototype.toSliderValue = function (timeSpan) {
|
||||
var timeSystem = this.conductor.timeSystem();
|
||||
if (timeSystem) {
|
||||
var zoomDefaults = this.conductor.timeSystem().defaults().zoom;
|
||||
var perc = timeSpan / (zoomDefaults.min - zoomDefaults.max);
|
||||
return 1 - Math.pow(perc, 1 / 4);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a time span, set a label for the units of time that it,
|
||||
* roughly, represents. Leverages
|
||||
* @param {TimeSpan} timeSpan
|
||||
*/
|
||||
TimeConductorController.prototype.toTimeUnits = function (timeSpan) {
|
||||
if (this.conductor.timeSystem()) {
|
||||
var timeFormat = this.formatService.getFormat(this.conductor.timeSystem().formats()[0]);
|
||||
this.$scope.timeUnits = timeFormat.timeUnits && timeFormat.timeUnits(timeSpan);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Zooming occurs when the user manipulates the zoom slider.
|
||||
* Zooming updates the scale and bounds fields immediately, but does
|
||||
* not trigger a bounds change to other views until the mouse button
|
||||
* is released.
|
||||
* @param bounds
|
||||
*/
|
||||
TimeConductorController.prototype.onZoom = function (sliderValue) {
|
||||
var zoomDefaults = this.conductor.timeSystem().defaults().zoom;
|
||||
var timeSpan = Math.pow((1 - sliderValue), 4) * (zoomDefaults.min - zoomDefaults.max);
|
||||
|
||||
var zoom = this.conductorViewService.zoom(timeSpan);
|
||||
|
||||
this.$scope.boundsModel.start = zoom.bounds.start;
|
||||
this.$scope.boundsModel.end = zoom.bounds.end;
|
||||
this.toTimeUnits(zoom.bounds.end - zoom.bounds.start);
|
||||
|
||||
if (zoom.deltas) {
|
||||
this.setFormFromDeltas(zoom.deltas);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fired when user has released the zoom slider
|
||||
* @event platform.features.conductor.TimeConductorController~zoomStop
|
||||
*/
|
||||
/**
|
||||
* Invoked when zoom slider is released by user. Will update the time conductor with the new bounds, triggering
|
||||
* a global bounds change event.
|
||||
* @fires platform.features.conductor.TimeConductorController~zoomStop
|
||||
*/
|
||||
TimeConductorController.prototype.onZoomStop = function () {
|
||||
this.updateBoundsFromForm(this.$scope.boundsModel);
|
||||
this.updateDeltasFromForm(this.$scope.boundsModel);
|
||||
this.zooming = false;
|
||||
|
||||
this.conductorViewService.emit('zoom-stop');
|
||||
};
|
||||
|
||||
/**
|
||||
* Panning occurs when the user grabs the conductor scale and drags
|
||||
* it left or right to slide the window of time represented by the
|
||||
* conductor. Panning updates the scale and bounds fields
|
||||
* immediately, but does not trigger a bounds change to other views
|
||||
* until the mouse button is released.
|
||||
* @param {TimeConductorBounds} bounds
|
||||
*/
|
||||
TimeConductorController.prototype.onPan = function (bounds) {
|
||||
this.panning = true;
|
||||
this.$scope.boundsModel.start = bounds.start;
|
||||
this.$scope.boundsModel.end = bounds.end;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user releases the mouse button after panning.
|
||||
*/
|
||||
TimeConductorController.prototype.onPanStop = function () {
|
||||
this.panning = false;
|
||||
};
|
||||
|
||||
return TimeConductorController;
|
||||
}
|
||||
);
|
||||
|
@ -28,6 +28,8 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
var mockConductorViewService;
|
||||
var mockTimeSystems;
|
||||
var controller;
|
||||
var mockFormatService;
|
||||
var mockFormat;
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj("$scope", [
|
||||
@ -52,36 +54,33 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
"availableModes",
|
||||
"mode",
|
||||
"availableTimeSystems",
|
||||
"deltas"
|
||||
"deltas",
|
||||
"deltas",
|
||||
"on",
|
||||
"off"
|
||||
]
|
||||
);
|
||||
mockConductorViewService.availableModes.andReturn([]);
|
||||
mockConductorViewService.availableTimeSystems.andReturn([]);
|
||||
|
||||
mockFormatService = jasmine.createSpyObj('formatService', [
|
||||
'getFormat'
|
||||
]);
|
||||
mockFormat = jasmine.createSpyObj('format', [
|
||||
'format'
|
||||
]);
|
||||
mockFormatService.getFormat.andReturn(mockFormat);
|
||||
|
||||
mockTimeSystems = [];
|
||||
});
|
||||
|
||||
function getListener(name) {
|
||||
return mockTimeConductor.on.calls.filter(function (call) {
|
||||
return call.args[0] === name;
|
||||
function getListener(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
describe("", function () {
|
||||
beforeEach(function () {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
mockConductorViewService,
|
||||
mockTimeSystems
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("when time conductor state changes", function () {
|
||||
var mockFormat;
|
||||
var mockDeltaFormat;
|
||||
var defaultBounds;
|
||||
var defaultDeltas;
|
||||
@ -119,17 +118,18 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
{conductor: mockTimeConductor},
|
||||
mockConductorViewService,
|
||||
mockTimeSystems
|
||||
mockTimeSystems,
|
||||
mockFormatService
|
||||
);
|
||||
|
||||
tsListener = getListener("timeSystem");
|
||||
tsListener = getListener(mockTimeConductor.on, "timeSystem");
|
||||
});
|
||||
|
||||
it("listens for changes to conductor state", function () {
|
||||
expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
|
||||
expect(mockTimeConductor.on).toHaveBeenCalledWith("bounds", controller.setFormFromBounds);
|
||||
expect(mockTimeConductor.on).toHaveBeenCalledWith("bounds", controller.changeBounds);
|
||||
});
|
||||
|
||||
it("deregisters conductor listens when scope is destroyed", function () {
|
||||
@ -137,7 +137,7 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
|
||||
controller.destroy();
|
||||
expect(mockTimeConductor.off).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
|
||||
expect(mockTimeConductor.off).toHaveBeenCalledWith("bounds", controller.setFormFromBounds);
|
||||
expect(mockTimeConductor.off).toHaveBeenCalledWith("bounds", controller.changeBounds);
|
||||
});
|
||||
|
||||
it("when time system changes, sets time system on scope", function () {
|
||||
@ -151,7 +151,11 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
});
|
||||
|
||||
it("when time system changes, sets defaults on scope", function () {
|
||||
expect(tsListener).toBeDefined();
|
||||
mockDefaults.zoom = {
|
||||
min: 100,
|
||||
max: 10
|
||||
};
|
||||
mockTimeConductor.timeSystem.andReturn(timeSystem);
|
||||
tsListener(timeSystem);
|
||||
|
||||
expect(mockScope.boundsModel.start).toEqual(defaultBounds.start);
|
||||
@ -159,6 +163,32 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
|
||||
expect(mockScope.boundsModel.startDelta).toEqual(defaultDeltas.start);
|
||||
expect(mockScope.boundsModel.endDelta).toEqual(defaultDeltas.end);
|
||||
|
||||
expect(mockScope.timeSystemModel.minZoom).toBe(mockDefaults.zoom.min);
|
||||
expect(mockScope.timeSystemModel.maxZoom).toBe(mockDefaults.zoom.max);
|
||||
});
|
||||
|
||||
it("when bounds change, sets the correct zoom slider value", function () {
|
||||
var bounds = {
|
||||
start: 0,
|
||||
end: 50
|
||||
};
|
||||
mockDefaults.zoom = {
|
||||
min: 100,
|
||||
max: 0
|
||||
};
|
||||
|
||||
function exponentializer(rawValue) {
|
||||
return 1 - Math.pow(rawValue, 1 / 4);
|
||||
}
|
||||
|
||||
mockTimeConductor.timeSystem.andReturn(timeSystem);
|
||||
//Set zoom defaults
|
||||
tsListener(timeSystem);
|
||||
|
||||
controller.changeBounds(bounds);
|
||||
expect(controller.currentZoom).toEqual(exponentializer(0.5));
|
||||
|
||||
});
|
||||
|
||||
it("when bounds change, sets them on scope", function () {
|
||||
@ -167,7 +197,7 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
end: 2
|
||||
};
|
||||
|
||||
var boundsListener = getListener("bounds");
|
||||
var boundsListener = getListener(mockTimeConductor.on, "bounds");
|
||||
expect(boundsListener).toBeDefined();
|
||||
boundsListener(bounds);
|
||||
|
||||
@ -225,9 +255,10 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
{conductor: mockTimeConductor},
|
||||
mockConductorViewService,
|
||||
mockTimeSystemConstructors
|
||||
mockTimeSystemConstructors,
|
||||
mockFormatService
|
||||
);
|
||||
|
||||
mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems);
|
||||
@ -240,9 +271,10 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
{conductor: mockTimeConductor},
|
||||
mockConductorViewService,
|
||||
mockTimeSystemConstructors
|
||||
mockTimeSystemConstructors,
|
||||
mockFormatService
|
||||
);
|
||||
|
||||
mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems);
|
||||
@ -264,9 +296,10 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
{conductor: mockTimeConductor},
|
||||
mockConductorViewService,
|
||||
mockTimeSystemConstructors
|
||||
mockTimeSystemConstructors,
|
||||
mockFormatService
|
||||
);
|
||||
|
||||
controller.updateBoundsFromForm(formModel);
|
||||
@ -286,9 +319,10 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
{conductor: mockTimeConductor},
|
||||
mockConductorViewService,
|
||||
mockTimeSystemConstructors
|
||||
mockTimeSystemConstructors,
|
||||
mockFormatService
|
||||
);
|
||||
|
||||
controller.updateDeltasFromForm(formModel);
|
||||
@ -321,14 +355,33 @@ define(['./TimeConductorController'], function (TimeConductorController) {
|
||||
controller = new TimeConductorController(
|
||||
mockScope,
|
||||
mockWindow,
|
||||
mockTimeConductor,
|
||||
{conductor: mockTimeConductor},
|
||||
mockConductorViewService,
|
||||
mockTimeSystems
|
||||
mockTimeSystems,
|
||||
mockFormatService
|
||||
);
|
||||
|
||||
controller.selectTimeSystemByKey('testTimeSystem');
|
||||
expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(timeSystem, defaultBounds);
|
||||
});
|
||||
|
||||
it("updates form bounds during pan events", function () {
|
||||
var testBounds = {
|
||||
start: 10,
|
||||
end: 20
|
||||
};
|
||||
|
||||
expect(controller.$scope.boundsModel.start).not.toBe(testBounds.start);
|
||||
expect(controller.$scope.boundsModel.end).not.toBe(testBounds.end);
|
||||
|
||||
expect(controller.conductorViewService.on).toHaveBeenCalledWith("pan",
|
||||
controller.onPan);
|
||||
|
||||
getListener(controller.conductorViewService.on, "pan")(testBounds);
|
||||
|
||||
expect(controller.$scope.boundsModel.start).toBe(testBounds.start);
|
||||
expect(controller.$scope.boundsModel.end).toBe(testBounds.end);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -28,13 +28,14 @@ define(
|
||||
* Supports mode-specific time conductor behavior.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform.features.conductor
|
||||
* @param {TimeConductorMetadata} metadata
|
||||
*/
|
||||
function TimeConductorMode(metadata, conductor, timeSystems) {
|
||||
this.conductor = conductor;
|
||||
|
||||
this.mdata = metadata;
|
||||
this.dlts = undefined;
|
||||
this.deltasVal = undefined;
|
||||
this.source = undefined;
|
||||
this.sourceUnlisten = undefined;
|
||||
this.systems = timeSystems;
|
||||
@ -141,6 +142,9 @@ define(
|
||||
return this.source;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TimeConductorMode.prototype.destroy = function () {
|
||||
this.conductor.off('timeSystem', this.changeTimeSystem);
|
||||
|
||||
@ -177,23 +181,66 @@ define(
|
||||
*/
|
||||
TimeConductorMode.prototype.deltas = function (deltas) {
|
||||
if (arguments.length !== 0) {
|
||||
var oldEnd = this.conductor.bounds().end;
|
||||
|
||||
if (this.dlts && this.dlts.end !== undefined) {
|
||||
//Calculate the previous raw end value (without delta)
|
||||
oldEnd = oldEnd - this.dlts.end;
|
||||
var bounds = this.calculateBoundsFromDeltas(deltas);
|
||||
this.deltasVal = deltas;
|
||||
if (this.metadata().key !== 'fixed') {
|
||||
this.conductor.bounds(bounds);
|
||||
}
|
||||
|
||||
this.dlts = deltas;
|
||||
|
||||
var newBounds = {
|
||||
start: oldEnd - this.dlts.start,
|
||||
end: oldEnd + this.dlts.end
|
||||
};
|
||||
|
||||
this.conductor.bounds(newBounds);
|
||||
}
|
||||
return this.dlts;
|
||||
return this.deltasVal;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param deltas
|
||||
* @returns {TimeConductorBounds}
|
||||
*/
|
||||
TimeConductorMode.prototype.calculateBoundsFromDeltas = function (deltas) {
|
||||
var oldEnd = this.conductor.bounds().end;
|
||||
|
||||
if (this.deltasVal && this.deltasVal.end !== undefined) {
|
||||
//Calculate the previous raw end value (without delta)
|
||||
oldEnd = oldEnd - this.deltasVal.end;
|
||||
}
|
||||
|
||||
var bounds = {
|
||||
start: oldEnd - deltas.start,
|
||||
end: oldEnd + deltas.end
|
||||
};
|
||||
return bounds;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} ZoomLevel
|
||||
* @property {TimeConductorBounds} bounds The calculated bounds based on the zoom level
|
||||
* @property {TimeConductorDeltas} deltas The calculated deltas based on the zoom level
|
||||
*/
|
||||
/**
|
||||
* Calculates bounds and deltas based on provided timeSpan. Collectively
|
||||
* the bounds and deltas will constitute the new zoom level.
|
||||
* @param {number} timeSpan time duration in ms.
|
||||
* @return {ZoomLevel} The new zoom bounds and delta calculated for the provided time span
|
||||
*/
|
||||
TimeConductorMode.prototype.calculateZoom = function (timeSpan) {
|
||||
var zoom = {};
|
||||
|
||||
// If a tick source is defined, then the concept of 'now' is
|
||||
// important. Calculate zoom based on 'now'.
|
||||
if (this.tickSource()) {
|
||||
zoom.deltas = {
|
||||
start: timeSpan,
|
||||
end: this.deltasVal.end
|
||||
};
|
||||
zoom.bounds = this.calculateBoundsFromDeltas(zoom.deltas);
|
||||
// Calculate bounds based on deltas;
|
||||
} else {
|
||||
var bounds = this.conductor.bounds();
|
||||
var center = bounds.start + ((bounds.end - bounds.start)) / 2;
|
||||
bounds.start = center - timeSpan / 2;
|
||||
bounds.end = center + timeSpan / 2;
|
||||
zoom.bounds = bounds;
|
||||
}
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
return TimeConductorMode;
|
||||
|
@ -22,25 +22,30 @@
|
||||
|
||||
define(
|
||||
[
|
||||
'EventEmitter',
|
||||
'./TimeConductorMode'
|
||||
],
|
||||
function (TimeConductorMode) {
|
||||
function (EventEmitter, TimeConductorMode) {
|
||||
|
||||
/**
|
||||
* A class representing the state of the time conductor view. This
|
||||
* exposes details of the UI that are not represented on the
|
||||
* TimeConductor API itself such as modes and deltas.
|
||||
*
|
||||
* @memberof platform.features.conductor
|
||||
* @param conductor
|
||||
* @param timeSystems
|
||||
* @constructor
|
||||
*/
|
||||
function TimeConductorViewService(conductor, timeSystems) {
|
||||
function TimeConductorViewService(openmct, timeSystems) {
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.systems = timeSystems.map(function (timeSystemConstructor) {
|
||||
return timeSystemConstructor();
|
||||
});
|
||||
|
||||
this.conductor = conductor;
|
||||
this.conductor = openmct.conductor;
|
||||
this.currentMode = undefined;
|
||||
|
||||
/**
|
||||
@ -97,6 +102,8 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
TimeConductorViewService.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Getter/Setter for the Time Conductor Mode. Modes determine the
|
||||
* behavior of the time conductor, especially with regards to the
|
||||
@ -144,7 +151,7 @@ define(
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {object} Delta
|
||||
* @typedef {object} TimeConductorDeltas
|
||||
* @property {number} start Used to set the start bound of the
|
||||
* TimeConductor on tick. A positive value that will be subtracted
|
||||
* from the value provided by a tick source to determine the start
|
||||
@ -171,7 +178,7 @@ define(
|
||||
* tick
|
||||
* - end: A time in ms after the timestamp of the last data received
|
||||
* which will be used to determine the 'end' bound on tick
|
||||
* @returns {Delta} current value of the deltas
|
||||
* @returns {TimeConductorDeltas} current value of the deltas
|
||||
*/
|
||||
TimeConductorViewService.prototype.deltas = function () {
|
||||
//Deltas stored on mode. Use .apply to preserve arguments
|
||||
@ -197,6 +204,26 @@ define(
|
||||
return this.currentMode.availableTimeSystems();
|
||||
};
|
||||
|
||||
/**
|
||||
* An event to indicate that zooming is taking place
|
||||
* @event platform.features.conductor.TimeConductorViewService~zoom
|
||||
* @property {ZoomLevel} zoom the new zoom level.
|
||||
*/
|
||||
/**
|
||||
* Zoom to given time span. Will fire a zoom event with new zoom
|
||||
* bounds. Zoom bounds emitted in this way are considered ephemeral
|
||||
* and should be overridden by any time conductor bounds events. Does
|
||||
* not set bounds globally.
|
||||
* @param {number} zoom A time duration in ms
|
||||
* @fires platform.features.conductor.TimeConductorViewService~zoom
|
||||
* @see module:openmct.TimeConductor#bounds
|
||||
*/
|
||||
TimeConductorViewService.prototype.zoom = function (timeSpan) {
|
||||
var zoom = this.currentMode.calculateZoom(timeSpan);
|
||||
this.emit("zoom", zoom);
|
||||
return zoom;
|
||||
};
|
||||
|
||||
return TimeConductorViewService;
|
||||
}
|
||||
);
|
||||
|
@ -87,7 +87,7 @@ define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||
|
||||
it("At a minimum supports fixed mode", function () {
|
||||
var mockTimeSystems = [mockConstructor(basicTimeSystem)];
|
||||
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||
viewService = new TimeConductorViewService({conductor: mockTimeConductor}, mockTimeSystems);
|
||||
|
||||
var availableModes = viewService.availableModes();
|
||||
expect(availableModes.fixed).toBeDefined();
|
||||
@ -102,7 +102,7 @@ define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||
};
|
||||
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
|
||||
|
||||
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||
viewService = new TimeConductorViewService({conductor: mockTimeConductor}, mockTimeSystems);
|
||||
|
||||
var availableModes = viewService.availableModes();
|
||||
expect(availableModes.realtime).toBeDefined();
|
||||
@ -117,7 +117,7 @@ define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||
};
|
||||
tickingTimeSystem.tickSources.andReturn([mockLADTickSource]);
|
||||
|
||||
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||
viewService = new TimeConductorViewService({conductor: mockTimeConductor}, mockTimeSystems);
|
||||
|
||||
var availableModes = viewService.availableModes();
|
||||
expect(availableModes.lad).toBeDefined();
|
||||
@ -132,7 +132,7 @@ define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||
"destroy"
|
||||
]);
|
||||
|
||||
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||
viewService = new TimeConductorViewService({conductor: mockTimeConductor}, mockTimeSystems);
|
||||
viewService.currentMode = oldMode;
|
||||
viewService.mode('fixed');
|
||||
expect(oldMode.destroy).toHaveBeenCalled();
|
||||
@ -149,7 +149,7 @@ define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||
};
|
||||
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
|
||||
|
||||
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||
viewService = new TimeConductorViewService({conductor: mockTimeConductor}, mockTimeSystems);
|
||||
|
||||
//Set time system to one known to support realtime mode
|
||||
mockTimeConductor.timeSystem.andReturn(tickingTimeSystem);
|
||||
@ -169,7 +169,7 @@ define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||
};
|
||||
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
|
||||
|
||||
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||
viewService = new TimeConductorViewService({conductor: mockTimeConductor}, mockTimeSystems);
|
||||
|
||||
//Set time system to one known to not support realtime mode
|
||||
mockTimeConductor.timeSystem.andReturn(basicTimeSystem);
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Controller for the Time of Interest element used in various views to display the TOI. Responsible for setting
|
||||
* the text label for the current TOI, and for toggling the (un)pinned state which determines whether the TOI
|
||||
* indicator is visible.
|
||||
* @constructor
|
||||
*/
|
||||
function TimeOfInterestController($scope, openmct, formatService) {
|
||||
this.conductor = openmct.conductor;
|
||||
this.formatService = formatService;
|
||||
this.format = undefined;
|
||||
this.toiText = undefined;
|
||||
this.$scope = $scope;
|
||||
|
||||
//Bind all class functions to 'this'
|
||||
Object.keys(TimeOfInterestController.prototype).filter(function (key) {
|
||||
return typeof TimeOfInterestController.prototype[key] === 'function';
|
||||
}).forEach(function (key) {
|
||||
this[key] = TimeOfInterestController.prototype[key].bind(this);
|
||||
}.bind(this));
|
||||
|
||||
this.conductor.on('timeOfInterest', this.changeTimeOfInterest);
|
||||
this.conductor.on('timeSystem', this.changeTimeSystem);
|
||||
if (this.conductor.timeSystem()) {
|
||||
this.changeTimeSystem(this.conductor.timeSystem());
|
||||
var toi = this.conductor.timeOfInterest();
|
||||
if (toi) {
|
||||
this.changeTimeOfInterest(toi);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$on('$destroy', this.destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the time of interest changes on the conductor. Will pin (display) the TOI indicator, and set the
|
||||
* text using the default formatter of the currently active Time System.
|
||||
* @private
|
||||
* @param {integer} toi Current time of interest in ms
|
||||
*/
|
||||
TimeOfInterestController.prototype.changeTimeOfInterest = function (toi) {
|
||||
if (toi !== undefined) {
|
||||
this.$scope.pinned = true;
|
||||
this.toiText = this.format.format(toi);
|
||||
} else {
|
||||
this.$scope.pinned = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* When time system is changed, update the formatter used to
|
||||
* display the current TOI label
|
||||
*/
|
||||
TimeOfInterestController.prototype.changeTimeSystem = function (timeSystem) {
|
||||
this.format = this.formatService.getFormat(timeSystem.formats()[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TimeOfInterestController.prototype.destroy = function () {
|
||||
this.conductor.off('timeOfInterest', this.changeTimeOfInterest);
|
||||
this.conductor.off('timeSystem', this.changeTimeSystem);
|
||||
};
|
||||
|
||||
/**
|
||||
* Will unpin (hide) the TOI indicator. Has the effect of setting the time of interest to `undefined` on the
|
||||
* Time Conductor
|
||||
*/
|
||||
TimeOfInterestController.prototype.dismiss = function () {
|
||||
this.conductor.timeOfInterest(undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends out a time of interest event with the effect of resetting
|
||||
* the TOI displayed in views.
|
||||
*/
|
||||
TimeOfInterestController.prototype.resync = function () {
|
||||
this.conductor.timeOfInterest(this.conductor.timeOfInterest());
|
||||
};
|
||||
|
||||
return TimeOfInterestController;
|
||||
}
|
||||
);
|
@ -0,0 +1,115 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./TimeOfInterestController'], function (TimeOfInterestController) {
|
||||
|
||||
describe("The time of interest controller", function () {
|
||||
var controller;
|
||||
var mockScope;
|
||||
var mockConductor;
|
||||
var mockFormatService;
|
||||
var mockTimeSystem;
|
||||
var mockFormat;
|
||||
|
||||
beforeEach(function () {
|
||||
mockConductor = jasmine.createSpyObj("conductor", [
|
||||
"on",
|
||||
"timeSystem"
|
||||
]);
|
||||
mockScope = jasmine.createSpyObj("scope", [
|
||||
"$on"
|
||||
]);
|
||||
|
||||
mockFormat = jasmine.createSpyObj("format", [
|
||||
"format"
|
||||
]);
|
||||
|
||||
mockFormatService = jasmine.createSpyObj("formatService", [
|
||||
"getFormat"
|
||||
]);
|
||||
|
||||
mockFormatService.getFormat.andReturn(mockFormat);
|
||||
|
||||
mockTimeSystem = {
|
||||
formats: function () {
|
||||
return ["mockFormat"];
|
||||
}
|
||||
};
|
||||
|
||||
controller = new TimeOfInterestController(mockScope, {conductor: mockConductor}, mockFormatService);
|
||||
});
|
||||
|
||||
function getCallback(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
it("Listens for changes to TOI", function () {
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("timeOfInterest", controller.changeTimeOfInterest);
|
||||
});
|
||||
|
||||
it("updates format when time system changes", function () {
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
|
||||
getCallback(mockConductor.on, "timeSystem")(mockTimeSystem);
|
||||
expect(controller.format).toBe(mockFormat);
|
||||
});
|
||||
|
||||
describe("When TOI changes", function () {
|
||||
var toi;
|
||||
var toiCallback;
|
||||
var formattedTOI;
|
||||
|
||||
beforeEach(function () {
|
||||
var timeSystemCallback = getCallback(mockConductor.on, "timeSystem");
|
||||
toi = 1;
|
||||
mockConductor.timeSystem.andReturn(mockTimeSystem);
|
||||
|
||||
//Set time system
|
||||
timeSystemCallback(mockTimeSystem);
|
||||
|
||||
toiCallback = getCallback(mockConductor.on, "timeOfInterest");
|
||||
formattedTOI = "formatted TOI";
|
||||
|
||||
mockFormatService.getFormat.andReturn("mockFormat");
|
||||
mockFormat.format.andReturn(formattedTOI);
|
||||
});
|
||||
it("Uses the time system formatter to produce TOI text", function () {
|
||||
toiCallback = getCallback(mockConductor.on, "timeOfInterest");
|
||||
//Set TOI
|
||||
toiCallback(toi);
|
||||
expect(mockFormat.format).toHaveBeenCalled();
|
||||
});
|
||||
it("Sets the time of interest text", function () {
|
||||
//Set TOI
|
||||
toiCallback(toi);
|
||||
expect(controller.toiText).toBe(formattedTOI);
|
||||
});
|
||||
it("Pins the time of interest", function () {
|
||||
//Set TOI
|
||||
toiCallback(toi);
|
||||
expect(mockScope.pinned).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -64,13 +64,17 @@ define([
|
||||
return this.sources;
|
||||
};
|
||||
|
||||
UTCTimeSystem.prototype.defaults = function (key) {
|
||||
UTCTimeSystem.prototype.defaults = function () {
|
||||
var now = Math.ceil(Date.now() / 1000) * 1000;
|
||||
var ONE_MINUTE = 60 * 1 * 1000;
|
||||
var FIFTY_YEARS = 50 * 365 * 24 * 60 * 60 * 1000;
|
||||
|
||||
return {
|
||||
key: 'utc-default',
|
||||
name: 'UTC time system defaults',
|
||||
deltas: {start: FIFTEEN_MINUTES, end: 0},
|
||||
bounds: {start: now - FIFTEEN_MINUTES, end: now}
|
||||
bounds: {start: now - FIFTEEN_MINUTES, end: now},
|
||||
zoom: {min: FIFTY_YEARS, max: ONE_MINUTE}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -164,7 +164,7 @@ $ueTimeConductorH: (33px, 18px, 20px);
|
||||
margin-left: 0;
|
||||
}
|
||||
.l-time-range-tick-label {
|
||||
@include webkitProp(transform, translateX(-50%));
|
||||
@include transform(translateX(-50%));
|
||||
color: $colorPlotLabelFg;
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
|
@ -77,7 +77,8 @@ define([
|
||||
"telemetryFormatter",
|
||||
"telemetryHandler",
|
||||
"throttle",
|
||||
"PLOT_FIXED_DURATION"
|
||||
"PLOT_FIXED_DURATION",
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -40,21 +40,16 @@
|
||||
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
|
||||
ng-repeat="subplot in plot.getSubPlots()">
|
||||
<div class="gl-plot-legend">
|
||||
<!-- ng-class is temporarily hard-coded in next element -->
|
||||
<span
|
||||
class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
<span class='plot-color-swatch'
|
||||
ng-style="{ 'background-color': plot.getColor($index) }">
|
||||
<span class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
<span class='plot-color-swatch'
|
||||
ng-style="{ 'background-color': plot.getColor($index) }">
|
||||
</span>
|
||||
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
|
||||
</span>
|
||||
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{axes[1].active.name}}
|
||||
@ -74,81 +69,96 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
|
||||
<div class="gl-plot-wrapper-display-area-and-x-axis">
|
||||
<mct-include key="'time-of-interest'"
|
||||
class="l-toi-holder show-val"
|
||||
ng-if="toiPerc"
|
||||
ng-class="{ 'pinned': toiPinned, 'val-to-left': toiPerc > 80 }"
|
||||
ng-style="{'left': toiPerc + '%'}"></mct-include>
|
||||
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssclass}}"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()"
|
||||
ng-click="plot.setMode(option); toggle.setState(false)"
|
||||
class="{{option.cssclass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssclass}}"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()"
|
||||
ng-click="plot.setMode(option); toggle.setState(false)"
|
||||
class="{{option.cssclass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -68,7 +68,8 @@ define(
|
||||
telemetryFormatter,
|
||||
telemetryHandler,
|
||||
throttle,
|
||||
PLOT_FIXED_DURATION
|
||||
PLOT_FIXED_DURATION,
|
||||
openmct
|
||||
) {
|
||||
var self = this,
|
||||
plotTelemetryFormatter =
|
||||
@ -81,6 +82,7 @@ define(
|
||||
lastRange,
|
||||
lastDomain,
|
||||
handle;
|
||||
var conductor = openmct.conductor;
|
||||
|
||||
// Populate the scope with axis information (specifically, options
|
||||
// available for each axis.)
|
||||
@ -181,6 +183,18 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
function changeTimeOfInterest(timeOfInterest) {
|
||||
if (timeOfInterest !== undefined) {
|
||||
var bounds = conductor.bounds();
|
||||
var range = bounds.end - bounds.start;
|
||||
$scope.toiPerc = ((timeOfInterest - bounds.start) / range) * 100;
|
||||
$scope.toiPinned = true;
|
||||
} else {
|
||||
$scope.toiPerc = undefined;
|
||||
$scope.toiPinned = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new subscription; telemetrySubscriber gets
|
||||
// to do the meaningful work here.
|
||||
function subscribe(domainObject) {
|
||||
@ -193,6 +207,9 @@ define(
|
||||
true // Lossless
|
||||
);
|
||||
replot();
|
||||
|
||||
changeTimeOfInterest(conductor.timeOfInterest());
|
||||
conductor.on("timeOfInterest", changeTimeOfInterest);
|
||||
}
|
||||
|
||||
// Release the current subscription (called when scope is destroyed)
|
||||
@ -200,6 +217,7 @@ define(
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
handle = undefined;
|
||||
conductor.off("timeOfInterest", changeTimeOfInterest);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,6 +260,7 @@ define(
|
||||
requery();
|
||||
}
|
||||
self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
|
||||
changeTimeOfInterest(conductor.timeOfInterest());
|
||||
}
|
||||
|
||||
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||
|
@ -40,7 +40,8 @@ define(
|
||||
mockDomainObject,
|
||||
mockSeries,
|
||||
mockStatusCapability,
|
||||
controller;
|
||||
controller,
|
||||
mockConductor;
|
||||
|
||||
function bind(method, thisObj) {
|
||||
return function () {
|
||||
@ -120,13 +121,23 @@ define(
|
||||
mockHandle.getRangeValue.andReturn(42);
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockConductor = jasmine.createSpyObj('conductor', [
|
||||
'on',
|
||||
'off',
|
||||
'bounds',
|
||||
'timeSystem',
|
||||
'timeOfInterest'
|
||||
]);
|
||||
|
||||
controller = new PlotController(
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockExportImageService,
|
||||
mockFormatter,
|
||||
mockHandler,
|
||||
mockThrottle
|
||||
mockThrottle,
|
||||
undefined,
|
||||
{conductor: mockConductor}
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -115,12 +115,12 @@ define([
|
||||
{
|
||||
"key": "HistoricalTableController",
|
||||
"implementation": HistoricalTableController,
|
||||
"depends": ["$scope", "telemetryHandler", "telemetryFormatter", "$timeout"]
|
||||
"depends": ["$scope", "telemetryHandler", "telemetryFormatter", "$timeout", "openmct"]
|
||||
},
|
||||
{
|
||||
"key": "RealtimeTableController",
|
||||
"implementation": RealtimeTableController,
|
||||
"depends": ["$scope", "telemetryHandler", "telemetryFormatter"]
|
||||
"depends": ["$scope", "telemetryHandler", "telemetryFormatter", "openmct"]
|
||||
},
|
||||
{
|
||||
"key": "TableOptionsController",
|
||||
|
@ -1,9 +1,12 @@
|
||||
<div ng-controller="HistoricalTableController" ng-class="{'loading': loading}">
|
||||
<div ng-controller="HistoricalTableController as tableController"
|
||||
ng-class="{'loading': loading}">
|
||||
<mct-table
|
||||
headers="headers"
|
||||
time-columns="tableController.timeColumns"
|
||||
rows="rows"
|
||||
enableFilter="true"
|
||||
enableSort="true"
|
||||
sort-column="defaultSort"
|
||||
class="tabular-holder has-control-bar">
|
||||
</mct-table>
|
||||
</div>
|
@ -48,12 +48,19 @@
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr ng-repeat="visibleRow in visibleRows track by visibleRow.rowIndex"
|
||||
ng-style="{
|
||||
top: visibleRow.offsetY + 'px',
|
||||
}">
|
||||
<tr ng-repeat-start="visibleRow in visibleRows track by visibleRow.rowIndex"
|
||||
ng-if="visibleRow.rowIndex === toiRowIndex"
|
||||
ng-style="{ top: visibleRow.offsetY + 'px' }"
|
||||
class="l-toi-tablerow">
|
||||
<td colspan="999">
|
||||
<mct-include key="'time-of-interest'"
|
||||
class="l-toi-holder pinned"></mct-include>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end
|
||||
ng-style="{ top: visibleRow.offsetY + 'px' }"
|
||||
ng-click="table.onRowClick($event, visibleRow.rowIndex) ">
|
||||
<td ng-repeat="header in displayHeaders"
|
||||
ng-style=" {
|
||||
width: columnWidths[$index] + 'px',
|
||||
|
@ -1,10 +1,12 @@
|
||||
<div ng-controller="RealtimeTableController">
|
||||
<div ng-controller="RealtimeTableController as tableController">
|
||||
<mct-table
|
||||
headers="headers"
|
||||
rows="rows"
|
||||
time-columns="tableController.timeColumns"
|
||||
enableFilter="true"
|
||||
enableSort="true"
|
||||
class="tabular-holder has-control-bar"
|
||||
sort-column="defaultSort"
|
||||
auto-scroll="true">
|
||||
</mct-table>
|
||||
</div>
|
@ -36,7 +36,7 @@ define(
|
||||
* @param telemetryFormatter
|
||||
* @constructor
|
||||
*/
|
||||
function HistoricalTableController($scope, telemetryHandler, telemetryFormatter, $timeout) {
|
||||
function HistoricalTableController($scope, telemetryHandler, telemetryFormatter, $timeout, openmct) {
|
||||
var self = this;
|
||||
|
||||
this.$timeout = $timeout;
|
||||
@ -49,7 +49,7 @@ define(
|
||||
}
|
||||
});
|
||||
|
||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter, openmct);
|
||||
}
|
||||
|
||||
HistoricalTableController.prototype = Object.create(TableController.prototype);
|
||||
|
@ -12,7 +12,7 @@ define(
|
||||
* @param element
|
||||
* @constructor
|
||||
*/
|
||||
function MCTTableController($scope, $timeout, element, exportService) {
|
||||
function MCTTableController($scope, $timeout, element, exportService, formatService, openmct) {
|
||||
var self = this;
|
||||
|
||||
this.$scope = $scope;
|
||||
@ -24,6 +24,16 @@ define(
|
||||
this.resultsHeader = this.element.find('.mct-table>thead').first();
|
||||
this.sizingTableBody = this.element.find('.sizing-table>tbody').first();
|
||||
this.$scope.sizingRow = {};
|
||||
this.conductor = openmct.conductor;
|
||||
this.toiFormatter = undefined;
|
||||
this.formatService = formatService;
|
||||
|
||||
//Bind all class functions to 'this'
|
||||
Object.keys(MCTTableController.prototype).filter(function (key) {
|
||||
return typeof MCTTableController.prototype[key] === 'function';
|
||||
}).forEach(function (key) {
|
||||
this[key] = MCTTableController.prototype[key].bind(this);
|
||||
}.bind(this));
|
||||
|
||||
this.scrollable.on('scroll', this.onScroll.bind(this));
|
||||
|
||||
@ -42,6 +52,9 @@ define(
|
||||
scope.sortColumn = undefined;
|
||||
scope.sortDirection = undefined;
|
||||
}
|
||||
if (scope.sortColumn !== undefined) {
|
||||
scope.sortDirection = "asc";
|
||||
}
|
||||
}
|
||||
|
||||
setDefaults($scope);
|
||||
@ -68,8 +81,12 @@ define(
|
||||
} else if ($scope.sortDirection === 'desc') {
|
||||
$scope.sortColumn = undefined;
|
||||
$scope.sortDirection = undefined;
|
||||
} else if ($scope.sortColumn !== undefined &&
|
||||
$scope.sortDirection === undefined) {
|
||||
$scope.sortDirection = 'asc';
|
||||
}
|
||||
self.setRows($scope.rows);
|
||||
self.setTimeOfInterest(self.conductor.timeOfInterest());
|
||||
};
|
||||
|
||||
/*
|
||||
@ -78,22 +95,60 @@ define(
|
||||
$scope.$watchCollection('filters', function () {
|
||||
self.setRows($scope.rows);
|
||||
});
|
||||
$scope.$watch('headers', this.setHeaders.bind(this));
|
||||
$scope.$watch('rows', this.setRows.bind(this));
|
||||
$scope.$watch('headers', this.setHeaders);
|
||||
$scope.$watch('rows', this.setRows);
|
||||
|
||||
/*
|
||||
* Listen for rows added individually (eg. for real-time tables)
|
||||
*/
|
||||
$scope.$on('add:row', this.addRow.bind(this));
|
||||
$scope.$on('remove:row', this.removeRow.bind(this));
|
||||
$scope.$on('add:row', this.addRow);
|
||||
$scope.$on('remove:row', this.removeRow);
|
||||
|
||||
/**
|
||||
* Populated from the default-sort attribute on MctTable
|
||||
* directive tag.
|
||||
*/
|
||||
$scope.$watch('sortColumn', $scope.toggleSort);
|
||||
|
||||
/*
|
||||
* Listen for resize events to trigger recalculation of table width
|
||||
*/
|
||||
$scope.resize = this.setElementSizes.bind(this);
|
||||
$scope.resize = this.setElementSizes;
|
||||
|
||||
/**
|
||||
* Scope variable that is populated from the 'time-columns'
|
||||
* attribute on the MctTable tag. Indicates which columns, while
|
||||
* sorted, can be used for indicated time of interest.
|
||||
*/
|
||||
$scope.$watch("timeColumns", function (timeColumns) {
|
||||
if (timeColumns) {
|
||||
this.destroyConductorListeners();
|
||||
|
||||
this.conductor.on('timeSystem', this.changeTimeSystem);
|
||||
this.conductor.on('timeOfInterest', this.setTimeOfInterest);
|
||||
this.conductor.on('bounds', this.changeBounds);
|
||||
|
||||
// If time system defined, set initially
|
||||
if (this.conductor.timeSystem()) {
|
||||
this.changeTimeSystem(this.conductor.timeSystem());
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
$scope.$on('$destroy', this.destroyConductorListeners);
|
||||
}
|
||||
|
||||
MCTTableController.prototype.destroyConductorListeners = function () {
|
||||
this.conductor.off('timeSystem', this.changeTimeSystem);
|
||||
this.conductor.off('timeOfInterest', this.setTimeOfInterest);
|
||||
this.conductor.off('bounds', this.changeBounds);
|
||||
};
|
||||
|
||||
MCTTableController.prototype.changeTimeSystem = function () {
|
||||
var format = this.conductor.timeSystem().formats()[0];
|
||||
this.toiFormatter = this.formatService.getFormat(format);
|
||||
};
|
||||
|
||||
/**
|
||||
* If auto-scroll is enabled, this function will scroll to the
|
||||
* bottom of the page
|
||||
@ -163,19 +218,54 @@ define(
|
||||
this.$scope.$digest();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return first visible row, based on current scroll state.
|
||||
* @private
|
||||
*/
|
||||
MCTTableController.prototype.firstVisible = function () {
|
||||
var target = this.scrollable[0],
|
||||
topScroll = target.scrollTop,
|
||||
firstVisible;
|
||||
|
||||
if (topScroll < this.$scope.headerHeight) {
|
||||
firstVisible = 0;
|
||||
} else {
|
||||
firstVisible = Math.floor(
|
||||
(topScroll - this.$scope.headerHeight) /
|
||||
this.$scope.rowHeight
|
||||
);
|
||||
}
|
||||
|
||||
return firstVisible;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return last visible row, based on current scroll state.
|
||||
* @private
|
||||
*/
|
||||
MCTTableController.prototype.lastVisible = function () {
|
||||
var target = this.scrollable[0],
|
||||
topScroll = target.scrollTop,
|
||||
bottomScroll = topScroll + target.offsetHeight,
|
||||
lastVisible;
|
||||
|
||||
lastVisible = Math.ceil(
|
||||
(bottomScroll - this.$scope.headerHeight) /
|
||||
this.$scope.rowHeight
|
||||
);
|
||||
return lastVisible;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets visible rows based on array
|
||||
* content and current scroll state.
|
||||
*/
|
||||
MCTTableController.prototype.setVisibleRows = function () {
|
||||
var self = this,
|
||||
target = this.scrollable[0],
|
||||
topScroll = target.scrollTop,
|
||||
bottomScroll = topScroll + target.offsetHeight,
|
||||
firstVisible,
|
||||
lastVisible,
|
||||
totalVisible,
|
||||
numberOffscreen,
|
||||
firstVisible,
|
||||
lastVisible,
|
||||
start,
|
||||
end;
|
||||
|
||||
@ -184,21 +274,8 @@ define(
|
||||
start = 0;
|
||||
end = this.$scope.displayRows.length;
|
||||
} else {
|
||||
//rows has exceeded display maximum, so may be necessary to
|
||||
// scroll
|
||||
if (topScroll < this.$scope.headerHeight) {
|
||||
firstVisible = 0;
|
||||
} else {
|
||||
firstVisible = Math.floor(
|
||||
(topScroll - this.$scope.headerHeight) /
|
||||
this.$scope.rowHeight
|
||||
);
|
||||
}
|
||||
lastVisible = Math.ceil(
|
||||
(bottomScroll - this.$scope.headerHeight) /
|
||||
this.$scope.rowHeight
|
||||
);
|
||||
|
||||
firstVisible = this.firstVisible();
|
||||
lastVisible = this.lastVisible();
|
||||
totalVisible = lastVisible - firstVisible;
|
||||
numberOffscreen = this.maxDisplayRows - totalVisible;
|
||||
start = firstVisible - Math.floor(numberOffscreen / 2);
|
||||
@ -294,37 +371,37 @@ define(
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MCTTableController.prototype.insertSorted = function (array, element) {
|
||||
var index = -1,
|
||||
self = this,
|
||||
sortKey = this.$scope.sortColumn;
|
||||
MCTTableController.prototype.binarySearch = function (searchArray, searchElement, min, max) {
|
||||
var sampleAt = Math.floor((max - min) / 2) + min;
|
||||
|
||||
function binarySearch(searchArray, searchElement, min, max) {
|
||||
var sampleAt = Math.floor((max - min) / 2) + min;
|
||||
|
||||
if (max < min) {
|
||||
return min; // Element is not in array, min gives direction
|
||||
}
|
||||
|
||||
switch (self.sortComparator(searchElement[sortKey].text,
|
||||
searchArray[sampleAt][sortKey].text)) {
|
||||
case -1:
|
||||
return binarySearch(searchArray, searchElement, min,
|
||||
sampleAt - 1);
|
||||
case 0 :
|
||||
return sampleAt;
|
||||
case 1 :
|
||||
return binarySearch(searchArray, searchElement,
|
||||
sampleAt + 1, max);
|
||||
}
|
||||
if (max < min) {
|
||||
return min; // Element is not in array, min gives direction
|
||||
}
|
||||
switch (this.sortComparator(searchElement,
|
||||
searchArray[sampleAt][this.$scope.sortColumn].text)) {
|
||||
case -1:
|
||||
return this.binarySearch(searchArray, searchElement, min,
|
||||
sampleAt - 1);
|
||||
case 0 :
|
||||
return sampleAt;
|
||||
case 1 :
|
||||
return this.binarySearch(searchArray, searchElement,
|
||||
sampleAt + 1, max);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MCTTableController.prototype.insertSorted = function (array, element) {
|
||||
var index = -1;
|
||||
|
||||
if (!this.$scope.sortColumn || !this.$scope.sortDirection) {
|
||||
//No sorting applied, push it on the end.
|
||||
index = array.length;
|
||||
} else {
|
||||
//Sort is enabled, perform binary search to find insertion point
|
||||
index = binarySearch(array, element, 0, array.length - 1);
|
||||
index = this.binarySearch(array, element[this.$scope.sortColumn].text, 0, array.length - 1);
|
||||
}
|
||||
if (index === -1) {
|
||||
array.unshift(element);
|
||||
@ -485,7 +562,19 @@ define(
|
||||
}
|
||||
|
||||
this.$scope.displayRows = this.filterAndSort(newRows || []);
|
||||
this.resize(newRows).then(this.setVisibleRows.bind(this));
|
||||
this.resize(newRows)
|
||||
.then(this.setVisibleRows)
|
||||
//Timeout following setVisibleRows to allow digest to
|
||||
// perform DOM changes, otherwise scrollTo won't work.
|
||||
.then(this.$timeout)
|
||||
.then(function () {
|
||||
//If TOI specified, scroll to it
|
||||
var timeOfInterest = this.conductor.timeOfInterest();
|
||||
if (timeOfInterest) {
|
||||
this.setTimeOfInterest(timeOfInterest);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -525,6 +614,70 @@ define(
|
||||
return rowsToFilter.filter(matchRow.bind(null, filters));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param displayRowIndex {number} The index in the displayed rows
|
||||
* to scroll to.
|
||||
*/
|
||||
MCTTableController.prototype.scrollToRow = function (displayRowIndex) {
|
||||
|
||||
var visible = displayRowIndex > this.firstVisible() && displayRowIndex < this.lastVisible();
|
||||
|
||||
if (!visible) {
|
||||
var scrollTop = displayRowIndex * this.$scope.rowHeight +
|
||||
this.$scope.headerHeight -
|
||||
(this.scrollable[0].offsetHeight / 2);
|
||||
this.scrollable[0].scrollTop = scrollTop;
|
||||
this.setVisibleRows();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update rows with new data. If filtering is enabled, rows
|
||||
* will be sorted before display.
|
||||
*/
|
||||
MCTTableController.prototype.setTimeOfInterest = function (newTOI) {
|
||||
var isSortedByTime =
|
||||
this.$scope.timeColumns &&
|
||||
this.$scope.timeColumns.indexOf(this.$scope.sortColumn) !== -1;
|
||||
|
||||
this.$scope.toiRowIndex = -1;
|
||||
|
||||
if (newTOI && isSortedByTime) {
|
||||
var formattedTOI = this.toiFormatter.format(newTOI);
|
||||
var rowIndex = this.binarySearch(
|
||||
this.$scope.displayRows,
|
||||
formattedTOI,
|
||||
0,
|
||||
this.$scope.displayRows.length - 1);
|
||||
|
||||
if (rowIndex > 0 && rowIndex < this.$scope.displayRows.length) {
|
||||
this.$scope.toiRowIndex = rowIndex;
|
||||
this.scrollToRow(this.$scope.toiRowIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* On zoom, pan, etc. reset TOI
|
||||
* @param bounds
|
||||
*/
|
||||
MCTTableController.prototype.changeBounds = function (bounds) {
|
||||
this.setTimeOfInterest(this.conductor.timeOfInterest());
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MCTTableController.prototype.onRowClick = function (event, rowIndex) {
|
||||
if (this.$scope.timeColumns.indexOf(this.$scope.sortColumn) !== -1) {
|
||||
var selectedTime = this.$scope.displayRows[rowIndex][this.$scope.sortColumn].text;
|
||||
if (selectedTime &&
|
||||
this.toiFormatter.validate(selectedTime) &&
|
||||
event.altKey) {
|
||||
this.conductor.timeOfInterest(this.toiFormatter.parse(selectedTime));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return MCTTableController;
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ define(
|
||||
* @param telemetryFormatter
|
||||
* @constructor
|
||||
*/
|
||||
function RealtimeTableController($scope, telemetryHandler, telemetryFormatter) {
|
||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
||||
function RealtimeTableController($scope, telemetryHandler, telemetryFormatter, openmct) {
|
||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter, openmct);
|
||||
|
||||
this.maxRows = 100000;
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ define(
|
||||
function TelemetryTableController(
|
||||
$scope,
|
||||
telemetryHandler,
|
||||
telemetryFormatter
|
||||
telemetryFormatter,
|
||||
openmct
|
||||
) {
|
||||
var self = this;
|
||||
|
||||
@ -54,6 +55,7 @@ define(
|
||||
this.table = new TableConfiguration($scope.domainObject,
|
||||
telemetryFormatter);
|
||||
this.changeListeners = [];
|
||||
this.conductor = openmct.conductor;
|
||||
|
||||
$scope.rows = [];
|
||||
|
||||
@ -63,13 +65,35 @@ define(
|
||||
self.registerChangeListeners();
|
||||
});
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
|
||||
// Unsubscribe when the plot is destroyed
|
||||
this.$scope.$on("$destroy", this.destroy.bind(this));
|
||||
this.$scope.$on("$destroy", this.destroy);
|
||||
this.timeColumns = [];
|
||||
|
||||
|
||||
this.sortByTimeSystem = this.sortByTimeSystem.bind(this);
|
||||
this.conductor.on('timeSystem', this.sortByTimeSystem);
|
||||
this.conductor.off('timeSystem', this.sortByTimeSystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Based on the selected time system, find a matching domain column
|
||||
* to sort by. By default will just match on key.
|
||||
* @param timeSystem
|
||||
*/
|
||||
TelemetryTableController.prototype.sortByTimeSystem = function (timeSystem) {
|
||||
var scope = this.$scope;
|
||||
scope.defaultSort = undefined;
|
||||
if (timeSystem) {
|
||||
this.table.columns.forEach(function (column) {
|
||||
if (column.domainMetadata && column.domainMetadata.key === timeSystem.metadata.key) {
|
||||
scope.defaultSort = column.getTitle();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
TelemetryTableController.prototype.unregisterChangeListeners = function () {
|
||||
this.changeListeners.forEach(function (listener) {
|
||||
return listener && listener();
|
||||
@ -148,20 +172,37 @@ define(
|
||||
this.setup();
|
||||
};
|
||||
|
||||
TelemetryTableController.prototype.populateColumns = function (telemetryMetadata) {
|
||||
this.table.populateColumns(telemetryMetadata);
|
||||
|
||||
//Identify time columns
|
||||
telemetryMetadata.forEach(function (metadatum) {
|
||||
//Push domains first
|
||||
(metadatum.domains || []).forEach(function (domainMetadata) {
|
||||
this.timeColumns.push(domainMetadata.name);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
var timeSystem = this.conductor.timeSystem();
|
||||
if (timeSystem) {
|
||||
this.sortByTimeSystem(timeSystem);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup table columns based on domain object metadata
|
||||
*/
|
||||
TelemetryTableController.prototype.setup = function () {
|
||||
var handle = this.handle,
|
||||
table = this.table,
|
||||
self = this;
|
||||
|
||||
if (handle) {
|
||||
this.timeColumns = [];
|
||||
handle.promiseTelemetryObjects().then(function () {
|
||||
self.$scope.headers = [];
|
||||
self.$scope.rows = [];
|
||||
table.populateColumns(handle.getMetadata());
|
||||
|
||||
self.populateColumns(handle.getMetadata());
|
||||
self.filterColumns();
|
||||
|
||||
// When table column configuration changes, (due to being
|
||||
|
@ -86,14 +86,25 @@ define(
|
||||
'$timeout',
|
||||
'$element',
|
||||
'exportService',
|
||||
'formatService',
|
||||
'openmct',
|
||||
MCTTableController
|
||||
],
|
||||
controllerAs: "table",
|
||||
scope: {
|
||||
headers: "=",
|
||||
rows: "=",
|
||||
enableFilter: "=?",
|
||||
enableSort: "=?",
|
||||
autoScroll: "=?"
|
||||
autoScroll: "=?",
|
||||
// Used to indicate which columns contain time data. This
|
||||
// will be used for determining when the table is sorted
|
||||
// by the column that can be used for time conductor
|
||||
// time of interest.
|
||||
timeColumns: "=?",
|
||||
// Indicate a column to sort on. Allows control of sort
|
||||
// via configuration (eg. for default sort column).
|
||||
sortColumn: "=?"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ define(
|
||||
mockAngularTimeout,
|
||||
mockTimeoutHandle,
|
||||
watches,
|
||||
mockConductor,
|
||||
controller;
|
||||
|
||||
function promise(value) {
|
||||
@ -47,6 +48,12 @@ define(
|
||||
};
|
||||
}
|
||||
|
||||
function getCallback(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
watches = {};
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
@ -108,13 +115,22 @@ define(
|
||||
mockTelemetryHandle.promiseTelemetryObjects.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.request.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.getTelemetryObjects.andReturn([]);
|
||||
mockTelemetryHandle.getMetadata.andReturn([]);
|
||||
|
||||
mockTelemetryHandler = jasmine.createSpyObj('telemetryHandler', [
|
||||
'handle'
|
||||
]);
|
||||
mockTelemetryHandler.handle.andReturn(mockTelemetryHandle);
|
||||
|
||||
controller = new TableController(mockScope, mockTelemetryHandler, mockTelemetryFormatter, mockAngularTimeout);
|
||||
mockConductor = jasmine.createSpyObj("conductor", [
|
||||
"timeSystem",
|
||||
"on",
|
||||
"off"
|
||||
]);
|
||||
|
||||
controller = new TableController(mockScope, mockTelemetryHandler,
|
||||
mockTelemetryFormatter, mockAngularTimeout, {conductor: mockConductor});
|
||||
|
||||
controller.table = mockTable;
|
||||
controller.handle = mockTelemetryHandle;
|
||||
});
|
||||
@ -233,6 +249,60 @@ define(
|
||||
});
|
||||
|
||||
});
|
||||
describe('After populating columns', function () {
|
||||
var metadata;
|
||||
beforeEach(function () {
|
||||
metadata = [{domains: [{name: 'time domain 1'}, {name: 'time domain 2'}]}, {domains: [{name: 'time domain 3'}, {name: 'time domain 4'}]}];
|
||||
controller.populateColumns(metadata);
|
||||
});
|
||||
|
||||
it('Automatically identifies time columns', function () {
|
||||
expect(controller.timeColumns.length).toBe(4);
|
||||
expect(controller.timeColumns[0]).toBe('time domain 1');
|
||||
});
|
||||
|
||||
it('Automatically sorts by time column that matches current' +
|
||||
' time system', function () {
|
||||
var key = 'time_domain_1',
|
||||
name = 'time domain 1',
|
||||
mockTimeSystem = {
|
||||
metadata: {
|
||||
key: key
|
||||
}
|
||||
};
|
||||
|
||||
mockTable.columns = [
|
||||
{
|
||||
domainMetadata: {
|
||||
key: key
|
||||
},
|
||||
getTitle: function () {
|
||||
return name;
|
||||
}
|
||||
},
|
||||
{
|
||||
domainMetadata: {
|
||||
key: 'anotherColumn'
|
||||
},
|
||||
getTitle: function () {
|
||||
return 'some other column';
|
||||
}
|
||||
},
|
||||
{
|
||||
domainMetadata: {
|
||||
key: 'thirdColumn'
|
||||
},
|
||||
getTitle: function () {
|
||||
return 'a third column';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
expect(mockConductor.on).toHaveBeenCalledWith('timeSystem', jasmine.any(Function));
|
||||
getCallback(mockConductor.on, 'timeSystem')(mockTimeSystem);
|
||||
expect(controller.$scope.defaultSort).toBe(name);
|
||||
});
|
||||
});
|
||||
describe('Yields thread', function () {
|
||||
var mockSeries,
|
||||
mockRow;
|
||||
|
@ -23,9 +23,10 @@
|
||||
define(
|
||||
[
|
||||
"zepto",
|
||||
"moment",
|
||||
"../../src/controllers/MCTTableController"
|
||||
],
|
||||
function ($, MCTTableController) {
|
||||
function ($, moment, MCTTableController) {
|
||||
|
||||
var MOCK_ELEMENT_TEMPLATE =
|
||||
'<div><div class="l-view-section scrolling">' +
|
||||
@ -40,7 +41,10 @@ define(
|
||||
watches,
|
||||
mockTimeout,
|
||||
mockElement,
|
||||
mockExportService;
|
||||
mockExportService,
|
||||
mockConductor,
|
||||
mockFormatService,
|
||||
mockFormat;
|
||||
|
||||
function promise(value) {
|
||||
return {
|
||||
@ -50,6 +54,12 @@ define(
|
||||
};
|
||||
}
|
||||
|
||||
function getCallback(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
watches = {};
|
||||
|
||||
@ -67,15 +77,33 @@ define(
|
||||
'exportCSV'
|
||||
]);
|
||||
|
||||
mockConductor = jasmine.createSpyObj('conductor', [
|
||||
'bounds',
|
||||
'timeOfInterest',
|
||||
'timeSystem',
|
||||
'on',
|
||||
'off'
|
||||
]);
|
||||
|
||||
mockScope.displayHeaders = true;
|
||||
mockTimeout = jasmine.createSpy('$timeout');
|
||||
mockTimeout.andReturn(promise(undefined));
|
||||
mockFormat = jasmine.createSpyObj('formatter', [
|
||||
'parse',
|
||||
'format'
|
||||
]);
|
||||
mockFormatService = jasmine.createSpyObj('formatService', [
|
||||
'getFormat'
|
||||
]);
|
||||
mockFormatService.getFormat.andReturn(mockFormat);
|
||||
|
||||
controller = new MCTTableController(
|
||||
mockScope,
|
||||
mockTimeout,
|
||||
mockElement,
|
||||
mockExportService
|
||||
mockExportService,
|
||||
mockFormatService,
|
||||
{conductor: mockConductor}
|
||||
);
|
||||
spyOn(controller, 'setVisibleRows').andCallThrough();
|
||||
});
|
||||
@ -86,6 +114,133 @@ define(
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith('rows', jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('destroys listeners on destruction', function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith('$destroy', controller.destroyConductorListeners);
|
||||
getCallback(mockScope.$on, '$destroy')();
|
||||
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('timeSystem', controller.changeTimeSystem);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('timeOfInterest', controller.setTimeOfInterest);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('bounds', controller.changeBounds);
|
||||
});
|
||||
|
||||
describe('The time of interest', function () {
|
||||
var rowsAsc = [];
|
||||
var rowsDesc = [];
|
||||
beforeEach(function () {
|
||||
rowsAsc = [
|
||||
{
|
||||
'col1': {'text': 'row1 col1 match'},
|
||||
'col2': {'text': '2012-10-31 00:00:00.000Z'},
|
||||
'col3': {'text': 'row1 col3'}
|
||||
},
|
||||
{
|
||||
'col1': {'text': 'row2 col1 match'},
|
||||
'col2': {'text': '2012-11-01 00:00:00.000Z'},
|
||||
'col3': {'text': 'row2 col3'}
|
||||
},
|
||||
{
|
||||
'col1': {'text': 'row3 col1'},
|
||||
'col2': {'text': '2012-11-03 00:00:00.000Z'},
|
||||
'col3': {'text': 'row3 col3'}
|
||||
},
|
||||
{
|
||||
'col1': {'text': 'row3 col1'},
|
||||
'col2': {'text': '2012-11-04 00:00:00.000Z'},
|
||||
'col3': {'text': 'row3 col3'}
|
||||
}
|
||||
];
|
||||
rowsDesc = [
|
||||
{
|
||||
'col1': {'text': 'row1 col1 match'},
|
||||
'col2': {'text': '2012-11-02 00:00:00.000Z'},
|
||||
'col3': {'text': 'row1 col3'}
|
||||
},
|
||||
{
|
||||
'col1': {'text': 'row2 col1 match'},
|
||||
'col2': {'text': '2012-11-01 00:00:00.000Z'},
|
||||
'col3': {'text': 'row2 col3'}
|
||||
},
|
||||
{
|
||||
'col1': {'text': 'row3 col1'},
|
||||
'col2': {'text': '2012-10-30 00:00:00.000Z'},
|
||||
'col3': {'text': 'row3 col3'}
|
||||
},
|
||||
{
|
||||
'col1': {'text': 'row3 col1'},
|
||||
'col2': {'text': '2012-10-29 00:00:00.000Z'},
|
||||
'col3': {'text': 'row3 col3'}
|
||||
}
|
||||
];
|
||||
mockScope.timeColumns = ['col2'];
|
||||
mockScope.sortColumn = 'col2';
|
||||
controller.toiFormatter = mockFormat;
|
||||
});
|
||||
it("is observed for changes", function () {
|
||||
//Mock setting time columns
|
||||
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
|
||||
|
||||
expect(mockConductor.on).toHaveBeenCalledWith('timeOfInterest',
|
||||
jasmine.any(Function));
|
||||
});
|
||||
describe("causes corresponding row to be highlighted", function () {
|
||||
it("when changed and rows sorted ascending", function () {
|
||||
var testDate = "2012-11-02 00:00:00.000Z";
|
||||
mockScope.rows = rowsAsc;
|
||||
mockScope.displayRows = rowsAsc;
|
||||
mockScope.sortDirection = 'asc';
|
||||
|
||||
var toi = moment.utc(testDate).valueOf();
|
||||
mockFormat.parse.andReturn(toi);
|
||||
mockFormat.format.andReturn(testDate);
|
||||
|
||||
//mock setting the timeColumns parameter
|
||||
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
|
||||
|
||||
var toiCallback = getCallback(mockConductor.on, 'timeOfInterest');
|
||||
toiCallback(toi);
|
||||
|
||||
expect(mockScope.toiRowIndex).toBe(2);
|
||||
});
|
||||
it("when changed and rows sorted descending", function () {
|
||||
var testDate = "2012-10-31 00:00:00.000Z";
|
||||
mockScope.rows = rowsDesc;
|
||||
mockScope.displayRows = rowsDesc;
|
||||
mockScope.sortDirection = 'desc';
|
||||
|
||||
var toi = moment.utc(testDate).valueOf();
|
||||
mockFormat.parse.andReturn(toi);
|
||||
mockFormat.format.andReturn(testDate);
|
||||
|
||||
//mock setting the timeColumns parameter
|
||||
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
|
||||
|
||||
var toiCallback = getCallback(mockConductor.on, 'timeOfInterest');
|
||||
toiCallback(toi);
|
||||
|
||||
expect(mockScope.toiRowIndex).toBe(2);
|
||||
});
|
||||
it("when rows are set and sorted ascending", function () {
|
||||
var testDate = "2012-11-02 00:00:00.000Z";
|
||||
mockScope.sortDirection = 'asc';
|
||||
|
||||
var toi = moment.utc(testDate).valueOf();
|
||||
mockFormat.parse.andReturn(toi);
|
||||
mockFormat.format.andReturn(testDate);
|
||||
mockConductor.timeOfInterest.andReturn(toi);
|
||||
|
||||
//mock setting the timeColumns parameter
|
||||
getCallback(mockScope.$watch, 'timeColumns')(['col2']);
|
||||
|
||||
//Mock setting the rows on scope
|
||||
var rowsCallback = getCallback(mockScope.$watch, 'rows');
|
||||
rowsCallback(rowsAsc);
|
||||
|
||||
expect(mockScope.toiRowIndex).toBe(2);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('rows', function () {
|
||||
var testRows = [];
|
||||
beforeEach(function () {
|
||||
@ -132,7 +287,7 @@ define(
|
||||
});
|
||||
|
||||
it('Supports adding rows individually', function () {
|
||||
var addRowFunc = mockScope.$on.calls[mockScope.$on.calls.length - 2].args[1],
|
||||
var addRowFunc = getCallback(mockScope.$on, 'add:row'),
|
||||
row4 = {
|
||||
'col1': {'text': 'row3 col1'},
|
||||
'col2': {'text': 'ghi'},
|
||||
@ -146,7 +301,7 @@ define(
|
||||
});
|
||||
|
||||
it('Supports removing rows individually', function () {
|
||||
var removeRowFunc = mockScope.$on.calls[mockScope.$on.calls.length - 1].args[1];
|
||||
var removeRowFunc = getCallback(mockScope.$on, 'remove:row');
|
||||
controller.setRows(testRows);
|
||||
expect(mockScope.displayRows.length).toBe(3);
|
||||
removeRowFunc(undefined, 2);
|
||||
@ -173,6 +328,10 @@ define(
|
||||
describe('sorting', function () {
|
||||
var sortedRows;
|
||||
|
||||
beforeEach(function () {
|
||||
sortedRows = [];
|
||||
});
|
||||
|
||||
it('Sorts rows ascending', function () {
|
||||
mockScope.sortColumn = 'col1';
|
||||
mockScope.sortDirection = 'asc';
|
||||
@ -204,6 +363,20 @@ define(
|
||||
expect(sortedRows[2].col2.text).toEqual('abc');
|
||||
});
|
||||
|
||||
it('Allows sort column to be changed externally by ' +
|
||||
'setting or changing sortBy attribute', function () {
|
||||
mockScope.displayRows = testRows;
|
||||
var sortByCB = getCallback(mockScope.$watch, 'sortColumn');
|
||||
sortByCB('col2');
|
||||
|
||||
expect(mockScope.sortDirection).toEqual('asc');
|
||||
|
||||
expect(mockScope.displayRows[0].col2.text).toEqual('abc');
|
||||
expect(mockScope.displayRows[1].col2.text).toEqual('def');
|
||||
expect(mockScope.displayRows[2].col2.text).toEqual('ghi');
|
||||
|
||||
});
|
||||
|
||||
// https://github.com/nasa/openmct/issues/910
|
||||
it('updates visible rows in scope', function () {
|
||||
var oldRows;
|
||||
@ -369,8 +542,6 @@ define(
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -36,6 +36,7 @@ define(
|
||||
mockConfiguration,
|
||||
watches,
|
||||
mockTableRow,
|
||||
mockConductor,
|
||||
controller;
|
||||
|
||||
function promise(value) {
|
||||
@ -106,7 +107,8 @@ define(
|
||||
'getDatum',
|
||||
'promiseTelemetryObjects',
|
||||
'getTelemetryObjects',
|
||||
'request'
|
||||
'request',
|
||||
'getMetadata'
|
||||
]);
|
||||
|
||||
// Arbitrary array with non-zero length, contents are not
|
||||
@ -115,13 +117,22 @@ define(
|
||||
mockTelemetryHandle.promiseTelemetryObjects.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.getDatum.andReturn({});
|
||||
mockTelemetryHandle.request.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.getMetadata.andReturn([]);
|
||||
|
||||
mockTelemetryHandler = jasmine.createSpyObj('telemetryHandler', [
|
||||
'handle'
|
||||
]);
|
||||
mockTelemetryHandler.handle.andReturn(mockTelemetryHandle);
|
||||
|
||||
controller = new TableController(mockScope, mockTelemetryHandler, mockTelemetryFormatter);
|
||||
mockConductor = jasmine.createSpyObj('conductor', [
|
||||
'on',
|
||||
'off',
|
||||
'bounds',
|
||||
'timeSystem',
|
||||
'timeOfInterest'
|
||||
]);
|
||||
|
||||
controller = new TableController(mockScope, mockTelemetryHandler, mockTelemetryFormatter, {conductor: mockConductor});
|
||||
controller.table = mockTable;
|
||||
controller.handle = mockTelemetryHandle;
|
||||
});
|
||||
|
@ -75,12 +75,6 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
return true;
|
||||
};
|
||||
|
||||
function throwOnError(validationResult) {
|
||||
if (validationResult !== true) {
|
||||
throw new Error(validationResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the follow mode of the time conductor. In follow mode the
|
||||
* time conductor ticks, regularly updating the bounds from a timing
|
||||
@ -127,8 +121,12 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
*/
|
||||
TimeConductor.prototype.bounds = function (newBounds) {
|
||||
if (arguments.length > 0) {
|
||||
throwOnError(this.validateBounds(newBounds));
|
||||
this.boundsVal = newBounds;
|
||||
var validationResult = this.validateBounds(newBounds);
|
||||
if (validationResult !== true) {
|
||||
throw new Error(validationResult);
|
||||
}
|
||||
//Create a copy to avoid direct mutation of conductor bounds
|
||||
this.boundsVal = JSON.parse(JSON.stringify(newBounds));
|
||||
/**
|
||||
* The start time, end time, or both have been updated.
|
||||
* @event bounds
|
||||
@ -136,8 +134,15 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
* @property {TimeConductorBounds} bounds
|
||||
*/
|
||||
this.emit('bounds', this.boundsVal);
|
||||
|
||||
// If a bounds change results in a TOI outside of the current
|
||||
// bounds, unset it
|
||||
if (this.toi < newBounds.start || this.toi > newBounds.end) {
|
||||
this.timeOfInterest(undefined);
|
||||
}
|
||||
}
|
||||
return this.boundsVal;
|
||||
//Return a copy to prevent direct mutation of time conductor bounds.
|
||||
return JSON.parse(JSON.stringify(this.boundsVal));
|
||||
};
|
||||
|
||||
/**
|
||||
@ -164,9 +169,6 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
* Time System
|
||||
* */
|
||||
this.emit('timeSystem', this.system);
|
||||
// Do something with bounds here. Try and convert between
|
||||
// time systems? Or just set defaults when time system changes?
|
||||
// eg.
|
||||
this.bounds(bounds);
|
||||
} else if (arguments.length === 1) {
|
||||
throw new Error('Must set bounds when changing time system');
|
||||
@ -177,7 +179,8 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
/**
|
||||
* Get or set the Time of Interest. The Time of Interest is the temporal
|
||||
* focus of the current view. It can be manipulated by the user from the
|
||||
* time conductor or from other views.
|
||||
* time conductor or from other views.The time of interest can
|
||||
* effectively be unset by assigning a value of 'undefined'.
|
||||
* @fires module:openmct.TimeConductor~timeOfInterest
|
||||
* @param newTOI
|
||||
* @returns {number} the current time of interest
|
||||
|
@ -52,19 +52,19 @@ define(['./TimeConductor'], function (TimeConductor) {
|
||||
bounds = {start: 0, end: 1};
|
||||
expect(tc.bounds()).not.toBe(bounds);
|
||||
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
|
||||
expect(tc.bounds()).toBe(bounds);
|
||||
expect(tc.bounds()).toEqual(bounds);
|
||||
});
|
||||
|
||||
it("Disallows setting of invalid bounds", function () {
|
||||
bounds = {start: 1, end: 0};
|
||||
expect(tc.bounds()).not.toBe(bounds);
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||
expect(tc.bounds()).not.toBe(bounds);
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
|
||||
bounds = {start: 1};
|
||||
expect(tc.bounds()).not.toBe(bounds);
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||
expect(tc.bounds()).not.toBe(bounds);
|
||||
expect(tc.bounds()).not.toEqual(bounds);
|
||||
});
|
||||
|
||||
it("Allows setting of time system with bounds", function () {
|
||||
@ -106,5 +106,17 @@ define(['./TimeConductor'], function (TimeConductor) {
|
||||
tc.follow(follow);
|
||||
expect(eventListener).toHaveBeenCalledWith(follow);
|
||||
});
|
||||
|
||||
it("If bounds are set and TOI lies inside them, do not change TOI", function () {
|
||||
tc.timeOfInterest(6);
|
||||
tc.bounds({start: 1, end: 10});
|
||||
expect(tc.timeOfInterest()).toEqual(6);
|
||||
});
|
||||
|
||||
it("If bounds are set and TOI lies outside them, reset TOI", function () {
|
||||
tc.timeOfInterest(11);
|
||||
tc.bounds({start: 1, end: 10});
|
||||
expect(tc.timeOfInterest()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user