Compare commits

...

35 Commits

Author SHA1 Message Date
charlesh88
7ddc7b625b Fixed wrong conflict resolutions; polish search
Fixes #2000
Fixes #2078
- .invisible instead of .off in search.html;
- Minor polishing to search;
2018-06-29 10:50:39 -07:00
charlesh88
f0079612c7 Merge branch 'master' into ui-2000
Fixes #2000
Fixes #2078

# Conflicts:
#	platform/commonUI/general/res/sass/controls/_controls.scss
#	platform/search/res/templates/search.html
2018-06-29 10:35:35 -07:00
Andrew Henry
07fb20c32f Json export fix (#2043)
* Refactoring export to json to use new style objects

* Added convenience function to Object API to get identifiers as strings

* Updated tests for ExportAsJSON

* Fixed bug with parent composition not being updated with new ID. Also rewrite IDs in configuration etc. Fixes #2042
2018-06-28 17:45:14 -07:00
Deep Tailor
90a6bbc13e Notebook integration deep (#1947)
* NASA - OPEN MCT NOTEBOOK UI PROTOTYPE CHALLENGE
https://www.topcoder.com/challenge-details/30059614/
Initial submission

* Code updates:
-Topcoder final fixes
-NASA review fixes

* drag and drop style fix, new entry focus, delete display fix

* NASA reported issues fixed:
objects saved in notebook, delete entry dialog, style files, and new entry from drag objects fixed.

* Annotation toolbar UI style fixes, added annotation functionality on new entry dialog

* painterro .map file issue fixed.

* NASA review fixes:

css files adjusted
notebook children tree removed
embed's title links to live object

* CouchDB documentation added

* CouchDB documentation added

Screenshots added.

* CouchDB setup documentation added

* Test case functional and cosmetic issues fixed.

* Test cases functional and cosmetic issues fixed.

* updated file saver library

* Code issues fixes:
NotificationLaunchIndicator deleted.
Inappropriate modifications to domain object models fixed.
Implemented $destroy listener on entryDnd directive.
Naming conventions fixed.
Unnecessary changes made to platform handled.
Painterro dependency handled
gulp verify fix.

* names and package fixes

* filenames fix

* [Notebook] Relocate to platform/features/notebook

* [Notebook] Remove obsolete README

* [Notebook] Restore original index.html

* [Notebook] Expose via openmct.plugins

* [Notebook] Remove demo entries

* [Notebook] Run gulp fixstyle

* [Notebook] Use dot notation instead of brackets

...for checkstyle

* [Notebook] Remove extra comma

* [Notebook] Run gulp fixstyle

* [Notebook] Use dot notation instead of brackets

...for checkstyle

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Fix lint issues

* [Notebook] Run gulp fixstyle

* [Notebook] Include painterro for tests

* [Notebook] Fix require config for painterro

* [Merge] WIP markup and styling

Fixes #1896
- Very much WIP, currently having issues with
hovering and jiggling

* [Merge] WIP markup and styling

Fixes #1896
- Very much WIP, attempting to convert
textarea to contenteditable;

* [Merge] JS debugging

Fixes #1896
- Very much WIP!

* [Merge] JS debugging

Fixes #1896
- Really, really WIP
- DnD doesn't work properly, and drag to
existing entry no longer works.

* [Front-end] Notebook thematic styling; test console

Fixes #1896
- Added thematic styles and config;
- Really, really, really WIP!!
- DnD doesn't work properly, and drag to
existing entry no longer works.

* [Merge] Cleanups in JS

Fixes #1896
- Removed and commented out logging statements

* [Merge] WIP SCSS and markup polishing

Fixes #1896
- Significant style and markup changes;
- Styles, layout, etc. relating to embed elements;
- Fixes in both notebook.html and embedControl.html;
- Class name normalization;

* [Merge] WIP Mods related to MCTModalNotebook.js

Fixes #1896
Fixes #1906

* fix drag and drop, delete entries

* [Front-end] Refined styling of entry embeds

Fixes #1896

* [Merge] Generalized hover hide/show of local controls

Fixes #1896

* [Merge] Generalized labeled icon-* elements

Fixes #1896

* [Frontend]  CSS normalizing, apply general styles in markup

Fixes #1896
- Notebook class names more individualized;
- Apply .labeled and .has-local-controls general classes;
- Apply .s-input-inline to contenteditable div;
- Look and feel cleanups for drag area and entry elements;

* added modifiedOn time for entries that are changed, and fixed issue regarding inner text being filled when new entry button clicked

* [Frontend]  CSS sanding and cleanups

Fixes #1896
- Removing unused classes;
- Finessed margin and padding;

* [Frontend]  Mobile styling

Fixes #1896
- Mod .has-local-controls to not apply when in touch context

* [Frontend]  WIP Mobile styling

Fixes #1896
- phone portrait entry layout optimization

* fix expand in layout, which was causing snapshot at expand

* [Frontend]  Fixes to search control

Fixes #1896
- Search control now more robust, added
.search-filter-by-type class selector;

* [Frontend]  Fix custom Selects

Fixes #1896
- Custom Selects now much more
solid, handle width compression better;

* remove duplicate code from MCTModalNotebook and roll changes into MCTTriggerModal

* [Frontend]  WIP Mobile styling

Fixes #1896
- Fixed general approach to portrait orientation in
mobile/_layout.scss to use media query;
- Fixed portrait layout in _notebook_base.scss
to use media query;

* prevent multiple new notebook entry divs from being created on open overlay, instead create on initialization

* [Frontend]  WIP Snapshot styling

Fixes #1896
- Better class names;
- Moved buttons in frame layout;

* remove frame layout duplicate and use frame.html

* fix issue of preserving line breaks when text is received from a persisted source

* add comments, clean out some code, and fix broken tests

* fix export image after merging with master

* include painterro in karma config

* Inlined templates for notebook

* disable view policy - to allow layouts to function - needs more investigation

* fix layout display overload, remove viewpolicy and notebookLayout.html. Fix delete error - issues found when deploying for testathon

* fix (not being able to focus on content editable div to add text, while in layout)
- when in layout, the first child of the outermost div is the only one that registers a click, this was causing an issue of not being able to edit notebook entries. My fix includes finding the first child of the div that registers the click and forcing a focus event.

* fix focus one new entry issue, cleanup of code related to finding elements, and write more reusable code

* abstract findElementById for reusability and improve performance from O^2 to O

* user findElementById in entrydnd

* change snapshot library to dom-to-image

* [Frontend]  WIP Snapshot styling

Fixes #1896
Fixes #1947
- Significant markup changes to template in ViewSnaphot.js
- WIP!!! Keeping own topic branch for now

* [Frontend]  WIP Snapshot styling

Fixes #1896
Fixes #1947
- Significant markup changes to ViewSnaphot.js;
- Change in imagery.scss to move non-layout styling
to appropriate class;

* Removed snapshot from version number to close sprint eagle

* Updated version number for Enterprise release

* Lock filesaver version (#1956)

Lock filesaver version as there have been a large number of broken
builds from what should be non-breaking version increases.

Fixes currently broken build.

* [Frontend]  Snapshot styling

Fixes #1896
Fixes #1947
- Final tweaks after rebase from
notebook-integration-deep-styling

* fixes issue of overlay not closing when context menu item in clicked when viewing snapshot

* [Frontend]  Painterro styling

Fixes #1896
Fixes #1947
- WIP
- Painterro styling overrides and config
- Removed commented code

* [Frontend]  Painterro styling

Fixes #1896
Fixes #1947
- WIP
- Painterro styling overrides and config

* fixes issue of overlay not closing when context menu item in clicked when viewing snapshot

* specify require paths for new library

* [Frontend]  Local controls CSS added for hide/show of trash can icons

Fixes #1896
Fixes #1947
- Also updated frame.scss to use same transition timing

* proper shimming

* dragging objects to notebook now only creates a link, clicking on snapshot from object view takes a snapshot of the current view, without re-rendering

* [Frontend]  Local controls CSS added for hide/show of trash can icons

Fixes #1896
Fixes #1947
- Also updated frame.scss to use same transition timing

* select correct div for snapshot

* [Frontend]  Adding background color to snapshot

Fixes #1896
Fixes #1947

* remove snapshot class after async image render

* [Frontend]  Adding background color to snapshot

Fixes #1896
Fixes #1947

* remove snapshot button from frames in layout

* remove snapshot from frame view, add it only to overlay, change mctSnapShot to accomodate taking snaps of overlay/object view

* add preview action, working, need styling for notebook action on preview

* fix checkstyle

* change glyph for preview, use similar tempalte to frame.html

* dont allow preview action on objects getting edited currently

* changes to browseController and NavigationService to block navigation and show preview of object when trying to navigate to object in tree in edit mode

* [Frontend]  Painterro styling and config

Fixes #1896
Fixes #1947
- Changes mainly related to toolbar styling and labels

* [Frontend]  Notebook/Preview related sanding and polishing

Fixes #1947
- Changed description for notebook-new-entry

* [Frontend]  Notebook/Preview related sanding and polishing

Fixes #1947
- Added new global "hide-in-t-main-view" class;
- Apply new class to Preview action to suppress
display of that button in main view of navigated object;

* code cleanup

* [Frontend]  Notebook/Preview related sanding and polishing

Fixes #1947
- Classes for Notebook Entry button spacing;

* abstract overlay into a service/api - to reduce code duplication
catch error produced by painterro because of async div creation by dialog service

* fix broken mcttriggermodal tests

* fix checkstyle and lint

* add functionality of being able to add buttons to the browse bar element of overlay when instantiating the overlay service

* Reduce frequency of template recompilation in mct-include

* Use updated painterro library. Fixes #1981

* add save flag and call done in both cases (clicking on cancel or ok)

* fixes #1951
persist modified empty entry on blur

* Bump Node Version

* fix checkstyle

* fixes issue where annotating snapshot that is already saved in notebook does not work

* fix painterro button styling issue
move jquery logic inside timeout block, because buttons are asynchronously created

* remove description required when saving snapshot to notebook

* remove create snapshot action from embeds, and add preview action to embeds

* fixes edge case for issue #1981
Add a reject callback in the edgecase that user presses the x icon or esc key to cancel annotation, which was leading to the drag drop issue

* Add default sort options on creation menu of Notebook

* fix auto focus on new entry when in oldest first order, both in layout and regular view

* [Frontend] Notebook mobile mods

- Hide entry area when mobile;
- Disallow entry edit or delete in mobile;

* fixes issue 2041 (#2049)

* fixes issue 2041
allows user to select caret position in notebook entries while in layout

* [Frontend] Restore class, refine selector

Fixes #2041
Fixes #2049
- Restored .s-input-inline to editable field;
- Refined pointer-events: none to properly target .title-label only;

* remove unused files/code and smoke test

* remove , add pre-wrap to css and use inner text

* make reviewer requested changes 'in progress still'

* make reviewer requested changes 'continued'

* replace html2canvas with dom-to-image
- add in progress dialog to export image service
- add error dialog to export image service

* Search UI refactored to use flex

Fixes #1947
- Fixes broken search inputs in main search and Notebook;
- Significant rewrite to search SCSS and markup;

* Fixes for Notebook custom selects; polishes to search

Fixes #1947
- Better flex styles for custom selects;
- Refinements to search styling;
- Much better mobile responsive layout for search and controls
in portrait layout;

* fix preview action for embeds, which was showing current domain object vs selected domain object

* Fixed hidden search dropdown menu

Fixes #1947

* Revert whitespace change

Revert change to whitespace in index.html.  #1947.

* [Export] Use html2canvas

Use html2canvas instead of dom-to-image.  Fixes issues with text
exports.  html2canvas is better supported and under active
development and is a better choice for this library.  Cleaned up
export code, ensure that images are properly saved as the correct
types.

related to feedback on #1947

* Don't show brackets when timestamp is not specified (#331)
2018-06-28 17:13:34 -07:00
Pegah Sarram
73e38f1955 [Toolbar] Implement a public API for adding toolbars (#1908)
* [API] Implement a toolbar registry and a plugin to allow providing a toolbar for a selected object.
* Modify the mct-toolbar directive to get the toolbar structure from a provider based on selection.
* Implements the layout toolbar in the layout bundle
2018-06-27 13:30:01 -07:00
Deep Tailor
de8f8d174d Add a string formatter (#2087)
* add StringFormat to formats
 fix #2086
2018-06-27 11:02:24 -07:00
Julie Wang
b187762d55 [Folder] List view load fix (#2069)
* [Folder] List view load fix

-List view data takes long time to display, has to do with Angular not triggering its digestive cycle when switching views
-Resolved issue by explicitly triggering the digestive cycle
-Fixes issue #2067

* Added comment to clarify code addition

fix #2069
2018-06-21 10:52:27 -07:00
Andrew Henry
45a152df86 Build columns from union of telemetry value metadata. (#2075)
* Build columns from union of telemetry value metadata. Do not manually clean up scope. Fixes #2027. Fixes #1884. Fixes #1817.

* Fixed tests that are failing on circle-ci

* Inlined getMetadataValues function
2018-06-21 10:50:43 -07:00
Charles Hacskaylo
8e6401bb52 Repaired approach to hiding Time Conductor Submit button
Fixes #2000
Fixes #2078
- Renamed .off to .invisible and added refinements;
2018-06-20 20:00:42 -07:00
Charles Hacskaylo
bd37ee9bf5 Remove commented styles/markup
Fixes #2000
Fixes #2078
2018-06-20 19:40:41 -07:00
Charles Hacskaylo
6c8806c4dc Re-added new icon-arrow-right-equilateral glyph;
Fixes #2000
Fixes #2078
2018-06-20 19:00:37 -07:00
Charles Hacskaylo
e1ad774d50 Merge latest master and resolve conflicts
Fixes #2000
Fixes #2078
- Conflicts with symbols font; will redo icon updates from ui-2000
2018-06-20 18:31:06 -07:00
David Milewicz
a3e78bbf91 Duplicate images (#2058)
* [Images] changed updateHistory to only compare the URL value and timestamp for equality
2018-06-20 14:26:13 -07:00
Deep Tailor
374e4afc32 allow clearing table filters with cancel button #1989 (#2005)
* fixes issue #1989 add cancel icon to filter input in table headers, which allows user to clear filter with one click
2018-06-20 14:18:54 -07:00
Julie Wang
2f8a0c2c2b [Notifications] Added timestamp and object name to copy notifications (#2060)
* [Notifications] Added timestamp and object name to copy notifications \n

-Fixes issue #331 
-Added timestamps to all notifications
-Added object name to copy success notification
-Updated placement and style of notification timestamp as discussed with Charles.
2018-06-20 10:37:19 -07:00
charlesh88
9067afe079 Better hiding of Time Conductor "Submit" button
Fixes #2000
2018-06-18 17:30:44 -07:00
charlesh88
8003d91064 Hide View Large button for nested hidden-frame Layouts
Fixes #2000
- When a layout is nested and has its frame hidden, hide the
View Large button;
2018-06-18 16:28:30 -07:00
charlesh88
10480a460a Refinements to Time Conductor
Fixes #2000
- Tweaks size of fixed position grab ticks;
- Positioning refinements to ticks and text;
- Hide major tick marks;
2018-06-18 15:53:45 -07:00
Charles Hacskaylo
fc07f10ff6 [Frontend] Fixed H2 in Elements pool
Fixes #2000
2018-06-18 14:25:09 -07:00
charlesh88
5677b84663 [Frontend] Minor tweaks to form and form-row
Fixes #2000
- Fixing margin problem with .form-row;
- h2 instead of div.section-header;
2018-06-18 14:25:09 -07:00
charlesh88
c0cb1edac6 [Frontend] Inspector refactor to CSS grid WIP
Fixes #2000
- Significant mods to CSS and markup;
- New grid archetypes classes;
- Added title attribs to plot options edit and browse props;
- Added value, alarm markers to plot series browse props;
- Nearly done (?) but needs unit testing and cleanups;
2018-06-18 14:25:09 -07:00
charlesh88
1040058028 [Frontend] Inspector refactor to CSS grid WIP
Fixes #1758
Fixes #2000
- WIP!!!;
- Good progress on Properties section;
2018-06-18 14:25:09 -07:00
charlesh88
1b8fcf4f9a [Frontend] Glyph improvements
Fixes #1758
Fixes #2000
- New arrow glyph for view controls;
- Increased hit area of view controls in tree for mobile and desktop;
- Better "hamburger" menu glyph;
2018-06-18 14:25:09 -07:00
charlesh88
a22130ba11 [Frontend] Tweaks to "nav up" arrow button
Fixes #1758
Fixes #2000
- Increased hit area of .l-back "nav up" arrow in object-browse-bar;
2018-06-18 14:25:09 -07:00
charlesh88
aa7954a3d2 [Frontend] Tweaks to pane collapse mini-tabs
Fixes #1758
Fixes #2000
- Enlarged mini-tabs and moved to screen top;
- Removed duplicative theme-based constants;
2018-06-18 14:25:09 -07:00
charlesh88
a7600a440c [Frontend] Better selecting in browse mode
Fixes #2000
- Better colors and approach to selecting in browse mode;
2018-06-18 14:25:09 -07:00
charlesh88
b488568136 [Frontend] local-controls classes
Fixes #2000
- Code cleanup;
- Update Style Guide content;
- Provisionally done, needs unit testing and double-checking;
2018-06-18 14:25:09 -07:00
charlesh88
5d6cbd6f20 [Frontend] WIP local-controls classes
Fixes #2000
- Slight increase in size to buttons in frame context;
2018-06-18 14:25:09 -07:00
charlesh88
3b8ad9ea7b [Frontend] WIP local-controls classes
Fixes #2000
- Use local-control classes in Timelines;
- Group Timeline buttons in l-btn-set;
- Use reset icon glyph;
2018-06-18 14:25:09 -07:00
charlesh88
77b87fa11e [Frontend] WIP local-controls classes
Fixes #2000
- Refactoring to use and apply local-control classes consistently;
- Plots and imagery done in main view and Display Layout contexts;
2018-06-18 14:25:09 -07:00
charlesh88
ffdcf0f504 [Frontend] WIP New local-controls classes
Fixes #2000
- h-local-controls
- Markup in plots changed
- Changed reset plot button's icon
2018-06-18 14:22:41 -07:00
Pete Richards
163043635e Merge pull request #2073 from nasa/table-spinners
Added digest after showing / hiding spinner. Fix #1882
2018-06-15 11:26:46 -07:00
Andrew Henry
ec9fd59d4a Added digest after showing / hiding spinner 2018-06-15 11:15:03 -07:00
Pete Richards
e03ea25392 Merge pull request #2065 from nasa/bind-to-loopback
Modified Express startup so that it only binds to loopback by default
2018-06-08 10:44:55 -07:00
Henry
56c16ed263 Modified Express startup so that it only binds to loopback by default 2018-06-08 09:54:18 -07:00
155 changed files with 5862 additions and 3104 deletions

5
app.js
View File

@@ -19,6 +19,7 @@
// Defaults
options.port = options.port || options.p || 8080;
options.host = options.host || options.h || 'localhost'
options.directory = options.directory || options.D || '.';
['include', 'exclude', 'i', 'x'].forEach(function (opt) {
options[opt] = options[opt] || [];
@@ -78,7 +79,7 @@
app.use(express['static'](options.directory));
// Finally, open the HTTP server and log the instance to the console
app.listen(options.port, function() {
console.log('Open MCT application running at localhost:' + options.port)
app.listen(options.port, options.host, function() {
console.log('Open MCT application running at %s:%s', options.host, options.port)
});
}());

View File

@@ -22,7 +22,6 @@
"eventemitter3": "^1.2.0",
"lodash": "3.10.1",
"almond": "~0.3.2",
"html2canvas": "^0.4.1",
"moment-timezone": "^0.5.13"
}
}
}

View File

@@ -149,12 +149,21 @@
<h2>Local Controls</h2>
<div class="cols cols1-1">
<div class="col">
<p>Local controls are typically buttons and selects that provide local control to an individual element. Typically, these controls are hidden in order to not block data display until the user hovers their cursor over an element, when the controls are displayed using a transition fade. Mousing out of the element fades the controls from view.</p>
<p>Local controls are typically buttons and selects that provide actions in close proximity to a component.</p>
<p>These controls can optionally be hidden to reduce clutter until the user hovers their cursor over an enclosing element. To use this approach, apply the class <code>.has-local-controls</code> to the element that should be aware of the hover and ensure that element encloses <code>.h-local-controls</code>.</p>
</div>
<mct-example><div class="plot-display-area" style="height: 100px; padding: 10px; position: relative;">Hover over me
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls">
<mct-example><div class="plot-display-area" style="padding: 10px; position: relative;">
Some content in here
<div class="h-local-controls h-local-controls-overlay-content l-btn-set">
<a class="s-button icon-arrow-left" title="Restore previous pan/zoom"></a>
<a class="s-button icon-arrows-out" title="Reset pan/zoom"></a>
<a class="s-button icon-reset" title="Reset pan/zoom"></a>
</div>
</div>
<div class="plot-display-area has-local-controls" style="padding: 10px; position: relative;">
Hover here
<div class="h-local-controls h-local-controls-overlay-content local-controls-hidden l-btn-set">
<a class="s-button icon-arrow-left" title="Restore previous pan/zoom"></a>
<a class="s-button icon-reset" title="Reset pan/zoom"></a>
</div>
</div></mct-example>
</div>

View File

@@ -69,6 +69,7 @@
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();

View File

@@ -38,6 +38,8 @@ module.exports = function(config) {
{pattern: 'node_modules/d3-*/**/*.js', included: false},
{pattern: 'node_modules/vue/**/*.js', included: false},
{pattern: 'src/**/*', included: false},
{pattern: 'node_modules/painterro/build/*.js', included: false},
{pattern: 'node_modules/html2canvas/dist/*', included: false},
{pattern: 'example/**/*.html', included: false},
{pattern: 'example/**/*.js', included: false},
{pattern: 'example/**/*.json', included: false},

View File

@@ -29,7 +29,6 @@ requirejs.config({
"csv": "bower_components/comma-separated-values/csv.min",
"EventEmitter": "bower_components/eventemitter3/index",
"es6-promise": "bower_components/es6-promise/es6-promise.min",
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
"moment": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
@@ -49,7 +48,9 @@ requirejs.config({
"d3-format": "node_modules/d3-format/build/d3-format.min",
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
"d3-time": "node_modules/d3-time/build/d3-time.min",
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min"
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
"html2canvas": "node_modules/html2canvas/dist/html2canvas.min",
"painterro": "node_modules/painterro/build/painterro.min"
},
"shim": {
"angular": {
@@ -61,12 +62,12 @@ requirejs.config({
"EventEmitter": {
"exports": "EventEmitter"
},
"html2canvas": {
"exports": "html2canvas"
},
"moment-duration-format": {
"deps": ["moment"]
},
"painterro": {
"exports": "Painterro"
},
"saveAs": {
"exports": "saveAs"
},
@@ -88,6 +89,9 @@ requirejs.config({
},
"d3-axis": {
"exports": "d3-axis"
},
"dom-to-image": {
"exports": "domtoimage"
}
}
});

View File

@@ -15,6 +15,7 @@
"d3-time-format": "2.1.x",
"express": "^4.13.1",
"minimist": "^1.1.1",
"painterro": "^0.2.65",
"request": "^2.69.0",
"vue": "^2.5.6"
},
@@ -30,6 +31,7 @@
"gulp-requirejs-optimize": "^0.3.1",
"gulp-sass": "^3.1.0",
"gulp-sourcemaps": "^1.6.0",
"html2canvas": "^1.0.0-alpha.12",
"jasmine-core": "^2.3.0",
"jscs-html-reporter": "^0.1.0",
"jsdoc": "^3.3.2",

View File

@@ -65,7 +65,7 @@
<div class='split-pane-component t-object pane primary-pane left'>
<mct-representation mct-object="navigatedObject"
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
class="abs holder holder-object">
class="abs holder holder-object t-main-view">
</mct-representation>
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"
title="{{ modelPaneInspect.visible()? 'Hide' : 'Show' }} the Inspection pane"

View File

@@ -19,41 +19,46 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div ng-controller="ObjectInspectorController as controller">
<ul class="flex-elem grows l-inspector-part">
<li>
<em class="t-inspector-part-header">Properties</em>
<div class="inspector-properties"
ng-repeat="data in metadata"
ng-class="{ first:$index === 0 }">
<div class="label">{{ data.name }}</div>
<div class="value">{{ data.value }}</div>
<div ng-controller="ObjectInspectorController as controller" class="grid-properties">
<ul class="l-inspector-part">
<h2 class="first">Properties</h2>
<li class="t-repeat grid-row"
ng-repeat="data in metadata"
ng-class="{ first:$index === 0 }">
<div class="grid-cell label">{{ data.name }}</div>
<div class="grid-cell value">{{ data.value }}</div>
</li>
</ul>
<ul class="l-inspector-part" ng-if="contextutalParents.length > 0">
<h2 title="The location of this linked object.">Location</h2>
<li class="grid-row">
<div class="label" ng-if="primaryParents.length > 0">This Link</div>
<div class="grid-cell value">
<div class="t-repeat inspector-location"
ng-repeat="parent in contextutalParents"
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</div>
</div>
</li>
<li ng-if="contextutalParents.length > 0">
<em class="t-inspector-part-header" title="The location of this linked object.">Location</em>
<div ng-if="primaryParents.length > 0" class="section-header">This Object</div>
<span class="inspector-location"
ng-repeat="parent in contextutalParents"
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>
</li>
<li ng-if="primaryParents.length > 0">
<div class="section-header">Object's Original</div>
<span class="inspector-location"
ng-repeat="parent in primaryParents"
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>
<li class="grid-row" ng-if="primaryParents.length > 0">
<div class="grid-cell label">Original</div>
<div class="grid-cell value">
<div class="t-repeat inspector-location value"
ng-repeat="parent in primaryParents"
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</div>
</div>
</li>
</ul>
</div>

View File

@@ -47,8 +47,8 @@ define(
urlService,
defaultPath
) {
var initialPath = ($route.current.params.ids || defaultPath).split("/");
var currentIds;
var initialPath = ($route.current.params.ids || defaultPath).split("/"),
currentIds;
$scope.treeModel = {
selectedObject: undefined,
@@ -56,7 +56,24 @@ define(
navigationService.setNavigation(object, true);
},
allowSelection: function (object) {
return navigationService.shouldNavigate();
var domainObjectInView = navigationService.getNavigation(),
isInEditMode = domainObjectInView.getCapability('status').get('editing');
if (isInEditMode) {
var actions = object.getCapability('action'),
previewAction = actions.getActions({key: 'mct-preview-action'})[0];
if (previewAction && previewAction.perform) {
previewAction.perform();
return false;
} else {
return navigationService.shouldNavigate();
}
} else {
return true;
}
}
};

View File

@@ -162,7 +162,6 @@ define(
*/
NavigationService.prototype.shouldWarnBeforeNavigate = function () {
var reasons = [];
this.checks.forEach(function (checkFn) {
var reason = checkFn();
if (reason) {

View File

@@ -4,7 +4,10 @@
<div class="top-bar">
<div class="title">{{ngModel.title}}</div>
</div>
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
<div class="hint" ng-hide="ngModel.hint === undefined">
{{ngModel.hint}}
<span ng-if="ngModel.timestamp !== undefined">[{{ngModel.timestamp}}]</span>
</div>
<div class="message-body">
<div class="message-action">
{{ngModel.actionText}}

View File

@@ -39,7 +39,6 @@ define([
"./src/policies/EditableMovePolicy",
"./src/policies/EditContextualActionPolicy",
"./src/representers/EditRepresenter",
"./src/representers/EditToolbarRepresenter",
"./src/capabilities/EditorCapability",
"./src/capabilities/TransactionCapabilityDecorator",
"./src/services/TransactionManager",
@@ -78,7 +77,6 @@ define([
EditableMovePolicy,
EditContextualActionPolicy,
EditRepresenter,
EditToolbarRepresenter,
EditorCapability,
TransactionCapabilityDecorator,
TransactionManager,
@@ -381,12 +379,6 @@ define([
"depends": [
"$log"
]
},
{
"implementation": EditToolbarRepresenter,
"depends": [
"openmct"
]
}
],
"constants": [
@@ -424,6 +416,17 @@ define([
"transactionService"
]
}
],
"runs": [
{
depends: [
"toolbars[]",
"openmct"
],
implementation: function (toolbars, openmct) {
toolbars.forEach(openmct.toolbars.addProvider, openmct.toolbars);
}
}
]
}
});

View File

@@ -24,7 +24,8 @@
<div class="items-select left flex-elem l-flex-row grows">
<mct-representation key="'back-arrow'"
mct-object="domainObject"
class="flex-elem l-back"></mct-representation>
class="flex-elem l-back">
</mct-representation>
<mct-representation key="'object-header'"
mct-object="domainObject"
class="l-flex-row flex-elem grows object-header">
@@ -48,8 +49,8 @@
<!-- Toolbar and Save/Cancel buttons -->
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
<mct-toolbar name="mctToolbar"
structure="toolbar.structure"
ng-model="toolbar.state"
structure="editToolbar.structure"
ng-model="editToolbar.state"
class="flex-elem grows">
</mct-toolbar>
<mct-representation key="'edit-action-buttons'"
@@ -61,7 +62,6 @@
<mct-representation key="representation.selected.key"
mct-object="representation.selected.key && domainObject"
class="abs flex-elem grows object-holder-main scroll"
toolbar="toolbar"
mct-selectable="{
item: domainObject.useCapability('adapter'),
oldItem: domainObject

View File

@@ -24,7 +24,7 @@
class="flex-elem holder"
ng-model="filterBy">
</mct-include>
<div class="flex-elem grows vscroll">
<div class="flex-elem grows vscroll scroll-pad">
<ul class="tree" id="inspector-elements-tree"
ng-if="composition.length > 0">
<li ng-repeat="containedObject in composition | filter:searchElements">

View File

@@ -20,192 +20,110 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
// Utility functions for reducing truth arrays
function and(a, b) {
return a && b;
}
function or(a, b) {
return a || b;
}
[
'../../../../../src/api/objects/object-utils',
'lodash'
],
function (
objectUtils,
_
) {
/**
* Provides initial structure and state (as suitable for provision
* to the `mct-toolbar` directive) for a view's tool bar, based on
* that view's declaration of what belongs in its tool bar and on
* to the `mct-toolbar` directive) for a view's toolbar, based on
* that view's declaration of what belongs in its toolbar and on
* the current selection.
*
* @param structure toolbar structure, as provided by view definition
* @param {Function} commit callback to invoke after changes
* @param $scope the Angular scope
* @param {Object} openmct the openmct object
* @param structure the toolbar structure
* @memberof platform/commonUI/edit
* @constructor
*/
function EditToolbar(structure, commit) {
function EditToolbar($scope, openmct, structure) {
this.toolbarStructure = [];
this.properties = [];
this.toolbarState = [];
this.openmct = openmct;
this.domainObjectsById = {};
this.unobserveObjects = [];
this.stateTracker = [];
$scope.$watchCollection(this.getState.bind(this), this.handleStateChanges.bind(this));
$scope.$on("$destroy", this.destroy.bind(this));
this.updateToolbar(structure);
this.registerListeners(structure);
}
/**
* Updates the toolbar with a new structure.
*
* @param {Array} structure the toolbar structure
*/
EditToolbar.prototype.updateToolbar = function (structure) {
var self = this;
// Generate a new key for an item's property
function addKey(property) {
self.properties.push(property);
function addKey(item) {
self.stateTracker.push({
id: objectUtils.makeKeyString(item.domainObject.identifier),
domainObject: item.domainObject,
property: item.property
});
self.properties.push(item.property);
return self.properties.length - 1; // Return index of property
}
// Invoke all functions in selections with the given name
function invoke(method, value) {
if (method) {
// Make the change in the selection
self.selection.forEach(function (selected) {
if (typeof selected[method] === 'function') {
selected[method](value);
}
});
// ...and commit!
commit();
}
}
// Prepare a toolbar item based on current selection
function convertItem(item) {
var converted = Object.create(item || {});
if (item.property) {
converted.key = addKey(item.property);
converted.key = addKey(item);
}
if (item.method) {
converted.click = function (v) {
invoke(item.method, v);
converted.click = function (value) {
item.method(value);
};
}
return converted;
}
// Prepare a toolbar section
function convertSection(section) {
var converted = Object.create(section || {});
converted.items =
((section || {}).items || [])
.map(convertItem);
return converted;
}
this.toolbarState = [];
this.selection = undefined;
this.properties = [];
this.toolbarStructure = Object.create(structure || {});
this.toolbarStructure.sections =
((structure || {}).sections || []).map(convertSection);
}
// Check if all elements of the selection which have this
// property have the same value for this property.
EditToolbar.prototype.isConsistent = function (property) {
var self = this,
consistent = true,
observed = false,
state;
// Check if a given element of the selection is consistent
// with previously-observed elements for this property.
function checkConsistency(selected) {
var next;
// Ignore selections which don't have this property
if (selected[property] !== undefined) {
// Look up state of this element in the selection
next = self.lookupState(property, selected);
// Detect inconsistency
if (observed) {
consistent = consistent && (next === state);
}
// Track state for next iteration
state = next;
observed = true;
}
}
// Iterate through selections
self.selection.forEach(checkConsistency);
return consistent;
};
// Used to filter out items which are applicable (or not)
// to the current selection.
EditToolbar.prototype.isApplicable = function (item) {
var property = (item || {}).property,
method = (item || {}).method,
exclusive = !!(item || {}).exclusive;
// Check if a selected item defines this property
function hasProperty(selected) {
return (property && (selected[property] !== undefined)) ||
(method && (typeof selected[method] === 'function'));
}
return this.selection.map(hasProperty).reduce(
exclusive ? and : or,
exclusive
) && this.isConsistent(property);
};
// Look up the current value associated with a property
EditToolbar.prototype.lookupState = function (property, selected) {
var value = selected[property];
return (typeof value === 'function') ? value() : value;
};
/**
* Set the current selection. Visibility of sections
* and items in the toolbar will be updated to match this.
* @param {Array} s the new selection
*/
EditToolbar.prototype.setSelection = function (s) {
var self = this;
// Show/hide controls in this section per applicability
function refreshSectionApplicability(section) {
var count = 0;
// Show/hide each item
(section.items || []).forEach(function (item) {
item.hidden = !self.isApplicable(item);
count += item.hidden ? 0 : 1;
});
// Hide this section if there are no applicable items
section.hidden = !count;
}
// Get initial value for a given property
function initializeState(property) {
var result;
// Look through all selections for this property;
// values should all match by the time we perform
// this lookup anyway.
self.selection.forEach(function (selected) {
result = (selected[property] !== undefined) ?
self.lookupState(property, selected) :
result;
structure.forEach(function (item) {
if (item.property === property) {
result = _.get(item.domainObject, item.property);
}
});
return result;
}
this.selection = s;
this.toolbarStructure.sections.forEach(refreshSectionApplicability);
// Tracks the domain object and property for every element in the state array
this.stateTracker = [];
this.toolbarStructure = structure.map(convertItem);
this.toolbarState = this.properties.map(initializeState);
};
/**
* Get the structure of the toolbar, as appropriate to
* Gets the structure of the toolbar, as appropriate to
* pass to `mct-toolbar`.
* @returns the toolbar structure
*
* @returns {Array} the toolbar structure
*/
EditToolbar.prototype.getStructure = function () {
return this.toolbarStructure;
};
/**
* Get the current state of the toolbar, as appropriate
* Gets the current state of the toolbar, as appropriate
* to two-way bind to the state handled by `mct-toolbar`.
*
* @returns {Array} state of the toolbar
*/
EditToolbar.prototype.getState = function () {
@@ -213,48 +131,124 @@ define(
};
/**
* Update state within the current selection.
* Mutates the domain object's property with a new value.
*
* @param {Object} dominObject the domain object
* @param {string} property the domain object's property to update
* @param value the property's new value
*/
EditToolbar.prototype.updateDomainObject = function (domainObject, property, value) {
this.openmct.objects.mutate(domainObject, property, value);
};
/**
* Updates state with the new value.
*
* @param {number} index the index of the corresponding
* element in the state array
* @param value the new value to convey to the selection
* @param value the new value to update the state array with
*/
EditToolbar.prototype.updateState = function (index, value) {
this.toolbarState[index] = value;
};
/**
* Register listeners for domain objects to watch for updates.
*
* @param {Array} the toolbar structure
*/
EditToolbar.prototype.registerListeners = function (structure) {
var self = this;
// Update value for this property in all elements of the
// selection which have this property.
function updateProperties(property, val) {
var changed = false;
// Update property in a selected element
function updateProperty(selected) {
// Ignore selected elements which don't have this property
if (selected[property] !== undefined) {
// Check if this is a setter, or just assignable
if (typeof selected[property] === 'function') {
changed =
changed || (selected[property]() !== val);
selected[property](val);
} else {
changed =
changed || (selected[property] !== val);
selected[property] = val;
}
}
}
// Update property in all selected elements
self.selection.forEach(updateProperty);
// Return whether or not anything changed
return changed;
function observeObject(domainObject, id) {
var unobserveObject = self.openmct.objects.observe(domainObject, '*', function (newObject) {
self.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject));
self.scheduleStateUpdate();
});
self.unobserveObjects.push(unobserveObject);
}
return updateProperties(this.properties[index], value);
structure.forEach(function (item) {
var domainObject = item.domainObject;
var id = objectUtils.makeKeyString(domainObject.identifier);
if (!self.domainObjectsById[id]) {
self.domainObjectsById[id] = {
domainObject: domainObject,
properties: []
};
observeObject(domainObject, id);
}
self.domainObjectsById[id].properties.push(item.property);
});
};
/**
* Delays updating the state.
*/
EditToolbar.prototype.scheduleStateUpdate = function () {
if (this.stateUpdateScheduled) {
return;
}
this.stateUpdateScheduled = true;
setTimeout(this.updateStateAfterMutation.bind(this));
};
EditToolbar.prototype.updateStateAfterMutation = function () {
this.stateTracker.forEach(function (state, index) {
if (!this.domainObjectsById[state.id].newObject) {
return;
}
var domainObject = this.domainObjectsById[state.id].domainObject;
var newObject = this.domainObjectsById[state.id].newObject;
var currentValue = _.get(domainObject, state.property);
var newValue = _.get(newObject, state.property);
state.domainObject = newObject;
if (currentValue !== newValue) {
this.updateState(index, newValue);
}
}, this);
Object.values(this.domainObjectsById).forEach(function (tracker) {
if (tracker.newObject) {
tracker.domainObject = tracker.newObject;
}
delete tracker.newObject;
});
this.stateUpdateScheduled = false;
};
/**
* Removes the listeners.
*/
EditToolbar.prototype.deregisterListeners = function () {
this.unobserveObjects.forEach(function (unobserveObject) {
unobserveObject();
});
this.unobserveObjects = [];
};
EditToolbar.prototype.handleStateChanges = function (state) {
(state || []).map(function (newValue, index) {
var domainObject = this.stateTracker[index].domainObject;
var property = this.stateTracker[index].property;
var currentValue = _.get(domainObject, property);
if (currentValue !== newValue) {
this.updateDomainObject(domainObject, property, newValue);
}
}, this);
};
EditToolbar.prototype.destroy = function () {
this.deregisterListeners();
};
return EditToolbar;
}
);

View File

@@ -1,154 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./EditToolbar', './EditToolbarSelection'],
function (EditToolbar, EditToolbarSelection) {
// No operation
var NOOP_REPRESENTER = {
represent: function () {},
destroy: function () {}
};
/**
* The EditToolbarRepresenter populates the toolbar in Edit mode
* based on a view's definition.
* @param {Scope} scope the Angular scope of the representation
* @memberof platform/commonUI/edit
* @constructor
* @implements {Representer}
*/
function EditToolbarRepresenter(openmct, scope, element, attrs) {
var self = this;
// Mark changes as ready to persist
function commit(message) {
if (scope.commit) {
scope.commit(message);
}
}
// Handle changes to the current selection
function updateSelection(selection) {
// Only update if there is a toolbar to update
if (self.toolbar) {
// Make sure selection is array-like
selection = Array.isArray(selection) ?
selection :
(selection ? [selection] : []);
// Update the toolbar's selection
self.toolbar.setSelection(selection);
// ...and expose its structure/state
self.toolbarObject.structure =
self.toolbar.getStructure();
self.toolbarObject.state =
self.toolbar.getState();
}
}
// Get state (to watch it)
function getState() {
return self.toolbarObject.state;
}
// Update selection models to match changed toolbar state
function updateState(state) {
// Update underlying state based on toolbar changes
var changed = (state || []).map(function (value, index) {
return self.toolbar.updateState(index, value);
}).reduce(function (a, b) {
return a || b;
}, false);
// Only commit if something actually changed
if (changed) {
// Commit the changes.
commit("Changes from toolbar.");
}
}
this.clearExposedToolbar = function () {
// Clear exposed toolbar state (if any)
if (attrs.toolbar) {
delete scope.$parent[attrs.toolbar];
}
};
this.exposeToolbar = function () {
scope.$parent[self.attrs.toolbar] = self.toolbarObject;
};
this.commit = commit;
this.attrs = attrs;
this.updateSelection = updateSelection;
this.toolbar = undefined;
this.toolbarObject = {};
this.openmct = openmct;
this.scope = scope;
// If this representation exposes a toolbar, set up watches
// to synchronize with it.
if (attrs && attrs.toolbar) {
// Detect and handle changes to state from the toolbar
scope.$watchCollection(getState, updateState);
// Watch for changes in the current selection state
scope.$watchCollection("selection.all()", updateSelection);
// Expose toolbar state under that name
scope.$parent[attrs.toolbar] = this.toolbarObject;
} else {
// No toolbar declared, so do nothing.
return NOOP_REPRESENTER;
}
}
// Represent a domain object using this definition
EditToolbarRepresenter.prototype.represent = function (representation) {
// Get the newest toolbar definition from the view
var definition = (representation || {}).toolbar || {};
// If we have been asked to expose toolbar state...
if (this.attrs.toolbar) {
// Initialize toolbar object
this.toolbar = new EditToolbar(definition, this.commit);
// Ensure toolbar state is exposed
this.exposeToolbar();
}
// Add toolbar selection to scope.
this.scope.selection = new EditToolbarSelection(
this.scope,
this.openmct
);
// Initialize toolbar to current selection
this.updateSelection(this.scope.selection.all());
};
// Destroy; remove toolbar object from parent scope
EditToolbarRepresenter.prototype.destroy = function () {
this.clearExposedToolbar();
};
return EditToolbarRepresenter;
}
);

View File

@@ -1,157 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Tracks selection state for editable views. Selection is
* implemented such that (from the toolbar's perspective)
* up to two objects can be "selected" at any given time:
*
* * The view proxy (see the `proxy` method), which provides
* an interface for interacting with the view itself (e.g.
* for buttons like "Add")
* * The selection, for single selected elements within the
* view.
*
* @memberof platform/commonUI/edit
* @constructor
*/
function EditToolbarSelection($scope, openmct) {
this.selection = [{}];
this.selecting = false;
this.selectedObj = undefined;
this.openmct = openmct;
var self = this;
function setSelection(selection) {
var selected = selection[0];
if (selected && selected.context.toolbar) {
self.select(selected.context.toolbar);
} else {
self.deselect();
}
if (selected && selected.context.viewProxy) {
self.proxy(selected.context.viewProxy);
}
setTimeout(function () {
$scope.$apply();
});
}
$scope.$on("$destroy", function () {
self.openmct.selection.off('change', setSelection);
});
this.openmct.selection.on('change', setSelection);
setSelection(this.openmct.selection.get());
}
/**
* Check if an object is currently selected.
* @param {*} obj the object to check for selection
* @returns {boolean} true if selected, otherwise false
*/
EditToolbarSelection.prototype.selected = function (obj) {
return (obj === this.selectedObj) || (obj === this.selection[0]);
};
/**
* Select an object.
* @param obj the object to select
* @returns {boolean} true if selection changed
*/
EditToolbarSelection.prototype.select = function (obj) {
// Proxy is always selected
if (obj === this.selection[0]) {
return false;
}
// Clear any existing selection
this.deselect();
// Note the current selection state
this.selectedObj = obj;
this.selecting = true;
// Add the selection
this.selection.push(obj);
};
/**
* Clear the current selection.
* @returns {boolean} true if selection changed
*/
EditToolbarSelection.prototype.deselect = function () {
// Nothing to do if we don't have a selected object
if (this.selecting) {
// Clear state tracking
this.selecting = false;
this.selectedObj = undefined;
// Remove the selection
this.selection.pop();
return true;
}
return false;
};
/**
* Get the currently-selected object.
* @returns the currently selected object
*/
EditToolbarSelection.prototype.get = function () {
return this.selectedObj;
};
/**
* Get/set the view proxy (for toolbar actions taken upon
* the view itself.)
* @param [proxy] the view proxy (if setting)
* @returns the current view proxy
*/
EditToolbarSelection.prototype.proxy = function (p) {
if (arguments.length > 0) {
this.selection[0] = p;
}
return this.selection[0];
};
/**
* Get an array containing all selections, including the
* selection proxy. It is generally not advisable to
* mutate this array directly.
* @returns {Array} all selections
*/
EditToolbarSelection.prototype.all = function () {
return this.selection;
};
return EditToolbarSelection;
}
);

View File

@@ -1,156 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../../src/representers/EditToolbarRepresenter"],
function (EditToolbarRepresenter) {
describe("The Edit mode toolbar representer", function () {
var mockScope,
mockElement,
testAttrs,
mockUnwatch,
representer,
mockOpenMCT,
mockSelection;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
'$scope',
['$on', '$watch', '$watchCollection', "commit", "$apply"]
);
mockElement = {};
testAttrs = { toolbar: 'testToolbar' };
mockScope.$parent = jasmine.createSpyObj(
'$parent',
['$watch', '$watchCollection']
);
mockUnwatch = jasmine.createSpy('unwatch');
mockScope.$parent.$watchCollection.andReturn(mockUnwatch);
mockSelection = jasmine.createSpyObj("selection", [
'on',
'off',
'get'
]);
mockSelection.get.andReturn([]);
mockOpenMCT = {
selection: mockSelection
};
representer = new EditToolbarRepresenter(
mockOpenMCT,
mockScope,
mockElement,
testAttrs
);
});
it("exposes toolbar state under a attr-defined name", function () {
// A structure/state object should have been added to the
// parent scope under the name provided in the "toolbar"
// attribute
expect(mockScope.$parent.testToolbar).toBeDefined();
});
it("is robust against lack of a toolbar definition", function () {
expect(function () {
representer.represent({});
}).not.toThrow();
});
it("watches for toolbar state changes", function () {
representer.represent({});
expect(mockScope.$watchCollection).toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Function)
);
expect(mockScope.$watchCollection.calls[0].args[0]())
.toBe(mockScope.$parent.testToolbar.state);
});
it("removes state from parent scope on destroy", function () {
// Verify precondition
expect(mockScope.$parent.testToolbar).toBeDefined();
// Destroy the representer
representer.destroy();
// Should have removed toolbar state from view
expect(mockScope.$parent.testToolbar).toBeUndefined();
});
// Verify a simple interaction between selection state and toolbar
// state; more complicated interactions are tested in EditToolbar.
it("conveys state changes", function () {
var testObject = { k: 123 };
// Provide a view which has a toolbar
representer.represent({
toolbar: { sections: [{ items: [{ property: 'k' }] }] }
});
// Update the selection
mockScope.selection.select(testObject);
expect(mockScope.$watchCollection.mostRecentCall.args[0])
.toEqual('selection.all()'); // Make sure we're using right watch
mockScope.$watchCollection.mostRecentCall.args[1]([testObject]);
// Update the state
mockScope.$parent.testToolbar.state[0] = 456;
// Invoke the first watch (assumed to be for toolbar state)
mockScope.$watchCollection.calls[0].args[1](
mockScope.$parent.testToolbar.state
);
// Should have updated the original object
expect(testObject.k).toEqual(456);
// Should have committed the change
expect(mockScope.commit).toHaveBeenCalled();
});
it("does not commit if nothing changed", function () {
var testObject = { k: 123 };
// Provide a view which has a toolbar
representer.represent({
toolbar: { sections: [{ items: [{ property: 'k' }] }] }
});
// Update the selection
mockScope.selection.select(testObject);
expect(mockScope.$watchCollection.mostRecentCall.args[0])
.toEqual('selection.all()'); // Make sure we're using right watch
mockScope.$watchCollection.mostRecentCall.args[1]([testObject]);
// Invoke the first watch (assumed to be for toolbar state)
mockScope.$watchCollection.calls[0].args[1](
mockScope.$parent.testToolbar.state
);
// Should have committed the change
expect(mockScope.commit).not.toHaveBeenCalled();
});
});
}
);

View File

@@ -1,128 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['../../src/representers/EditToolbarSelection'],
function (EditToolbarSelection) {
describe("The Edit mode selection manager", function () {
var testProxy,
testElement,
otherElement,
selection,
mockSelection,
mockOpenMCT,
mockScope;
beforeEach(function () {
testProxy = { someKey: "some value" };
testElement = { someOtherKey: "some other value" };
otherElement = { yetAnotherKey: 42 };
mockSelection = jasmine.createSpyObj("selection", [
// 'select',
'on',
'off',
'get'
]);
mockSelection.get.andReturn([]);
mockOpenMCT = {
selection: mockSelection
};
mockScope = jasmine.createSpyObj('$scope', [
'$on',
'$apply'
]);
selection = new EditToolbarSelection(mockScope, mockOpenMCT);
selection.proxy(testProxy);
});
it("adds the proxy to the selection array", function () {
expect(selection.all()).toEqual([testProxy]);
});
it("exposes view proxy", function () {
expect(selection.proxy()).toBe(testProxy);
});
it("includes selected objects alongside the proxy", function () {
selection.select(testElement);
expect(selection.all()).toEqual([testProxy, testElement]);
});
it("allows elements to be deselected", function () {
selection.select(testElement);
selection.deselect();
expect(selection.all()).toEqual([testProxy]);
});
it("replaces old selections with new ones", function () {
selection.select(testElement);
selection.select(otherElement);
expect(selection.all()).toEqual([testProxy, otherElement]);
});
it("allows retrieval of the current selection", function () {
selection.select(testElement);
expect(selection.get()).toBe(testElement);
selection.select(otherElement);
expect(selection.get()).toBe(otherElement);
});
it("can check if an element is selected", function () {
selection.select(testElement);
expect(selection.selected(testElement)).toBeTruthy();
expect(selection.selected(otherElement)).toBeFalsy();
selection.select(otherElement);
expect(selection.selected(testElement)).toBeFalsy();
expect(selection.selected(otherElement)).toBeTruthy();
});
it("considers the proxy to be selected", function () {
expect(selection.selected(testProxy)).toBeTruthy();
selection.select(testElement);
// Even when something else is selected...
expect(selection.selected(testProxy)).toBeTruthy();
});
it("treats selection of the proxy as a no-op", function () {
selection.select(testProxy);
expect(selection.all()).toEqual([testProxy]);
});
it("cleans up selection on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.mostRecentCall.args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
});
}
);

View File

@@ -25,7 +25,10 @@ define(
function (EditToolbar) {
describe("An Edit mode toolbar", function () {
var mockCommit,
var mockOpenMCT,
mockScope,
mockObjects,
mockDomainObject,
testStructure,
testAB,
testABC,
@@ -35,35 +38,30 @@ define(
testM,
toolbar;
function getVisibility(obj) {
return !obj.hidden;
}
beforeEach(function () {
mockCommit = jasmine.createSpy('commit');
testStructure = {
sections: [
{
items: [
{ name: "A", property: "a", exclusive: true },
{ name: "B", property: "b", exclusive: true },
{ name: "C", property: "c", exclusive: true }
]
},
{
items: [
{ name: "X", property: "x" },
{ name: "Y", property: "y", exclusive: true },
{ name: "Z", property: "z", exclusive: true }
]
},
{
items: [
{ name: "M", method: "m", exclusive: true }
]
}
]
};
mockOpenMCT = jasmine.createSpy('openmct', ['objects']);
mockObjects = jasmine.createSpyObj('objects', ['observe']);
mockObjects.observe.andReturn();
mockOpenMCT.objects = mockObjects;
mockScope = jasmine.createSpyObj("$scope", [
"$watchCollection",
"$on"
]);
mockScope.$watchCollection.andReturn();
mockDomainObject = jasmine.createSpyObj("domainObject", [
'identifier'
]);
testStructure = [
{ name: "A", property: "a", domainObject: mockDomainObject },
{ name: "B", property: "b", domainObject: mockDomainObject },
{ name: "C", property: "c", domainObject: mockDomainObject },
{ name: "X", property: "x", domainObject: mockDomainObject },
{ name: "Y", property: "y", domainObject: mockDomainObject },
{ name: "Z", property: "z", domainObject: mockDomainObject },
{ name: "M", method: "m", domainObject: mockDomainObject }
];
testAB = { a: 0, b: 1 };
testABC = { a: 0, b: 1, c: 2 };
testABC2 = { a: 4, b: 1, c: 2 }; // For inconsistent-state checking
@@ -71,151 +69,17 @@ define(
testABCYZ = { a: 0, b: 1, c: 2, y: 'Y!', z: 'Z!' };
testM = { m: jasmine.createSpy("method") };
toolbar = new EditToolbar(testStructure, mockCommit);
});
it("provides properties from the original structure", function () {
expect(
new EditToolbar(testStructure, [testABC])
.getStructure()
.sections[0]
.items[1]
.name
).toEqual("B");
});
// This is needed by mct-toolbar
it("adds keys to form structure", function () {
expect(
new EditToolbar(testStructure, [testABC])
.getStructure()
.sections[0]
.items[1]
.key
).not.toBeUndefined();
});
it("marks empty sections as hidden", function () {
// Verify that all sections are included when applicable...
toolbar.setSelection([testABCXYZ]);
expect(toolbar.getStructure().sections.map(getVisibility))
.toEqual([true, true, false]);
// ...but omitted when only some are applicable
toolbar.setSelection([testABC]);
expect(toolbar.getStructure().sections.map(getVisibility))
.toEqual([true, false, false]);
});
it("reads properties from selections", function () {
var structure, state;
toolbar.setSelection([testABC]);
structure = toolbar.getStructure();
state = toolbar.getState();
expect(state[structure.sections[0].items[0].key])
.toEqual(testABC.a);
expect(state[structure.sections[0].items[1].key])
.toEqual(testABC.b);
expect(state[structure.sections[0].items[2].key])
.toEqual(testABC.c);
});
it("reads properties from getters", function () {
var structure, state;
testABC.a = function () {
return "from a getter!";
};
toolbar.setSelection([testABC]);
structure = toolbar.getStructure();
state = toolbar.getState();
expect(state[structure.sections[0].items[0].key])
.toEqual("from a getter!");
});
it("sets properties on update", function () {
toolbar.setSelection([testABC]);
toolbar.updateState(
toolbar.getStructure().sections[0].items[0].key,
"new value"
);
// Should have updated the underlying object
expect(testABC.a).toEqual("new value");
});
it("invokes setters on update", function () {
var structure;
testABC.a = jasmine.createSpy('a');
toolbar.setSelection([testABC]);
structure = toolbar.getStructure();
toolbar.updateState(
structure.sections[0].items[0].key,
"new value"
);
// Should have updated the underlying object
expect(testABC.a).toHaveBeenCalledWith("new value");
});
it("provides a return value describing update status", function () {
// Should return true if actually updated, otherwise false
var key;
toolbar.setSelection([testABC]);
key = toolbar.getStructure().sections[0].items[0].key;
expect(toolbar.updateState(key, testABC.a)).toBeFalsy();
expect(toolbar.updateState(key, "new value")).toBeTruthy();
});
it("removes inapplicable items", function () {
// First, verify with all items
toolbar.setSelection([testABC]);
expect(toolbar.getStructure().sections[0].items.map(getVisibility))
.toEqual([true, true, true]);
// Then, try with some items omitted
toolbar.setSelection([testABC, testAB]);
expect(toolbar.getStructure().sections[0].items.map(getVisibility))
.toEqual([true, true, false]);
});
it("removes inconsistent states", function () {
// Only two of three values match among these selections
toolbar.setSelection([testABC, testABC2]);
expect(toolbar.getStructure().sections[0].items.map(getVisibility))
.toEqual([false, true, true]);
});
it("allows inclusive items", function () {
// One inclusive item is in the set, property 'x' of the
// second section; make sure items are pruned down
// when only some of the selection has x,y,z properties
toolbar.setSelection([testABC, testABCXYZ]);
expect(toolbar.getStructure().sections[1].items.map(getVisibility))
.toEqual([true, false, false]);
});
it("removes inclusive items when there are no matches", function () {
toolbar.setSelection([testABCYZ]);
expect(toolbar.getStructure().sections[1].items.map(getVisibility))
.toEqual([false, true, true]);
toolbar = new EditToolbar(mockScope, mockOpenMCT, testStructure);
});
it("adds click functions when a method is specified", function () {
toolbar.setSelection([testM]);
// Verify precondition
expect(testM.m).not.toHaveBeenCalled();
// Click!
toolbar.getStructure().sections[2].items[0].click();
// Should have called the underlying function
expect(testM.m).toHaveBeenCalled();
// Should also have committed the change
expect(mockCommit).toHaveBeenCalled();
var structure = toolbar.getStructure();
expect(structure[6].click).toBeDefined();
});
it("adds key for controls that define a property", function () {
var structure = toolbar.getStructure();
expect(structure[0].key).toEqual(0);
});
});
}

View File

@@ -115,8 +115,10 @@ define([
UTCTimeFormat.prototype.format = function (value) {
if (arguments.length > 1) {
return getScaledFormat(value);
} else {
} else if (value !== undefined) {
return moment.utc(value).format(DATE_FORMAT) + "Z";
} else {
return value;
}
};

View File

@@ -49,6 +49,8 @@ define([
"./src/directives/MCTSplitPane",
"./src/directives/MCTSplitter",
"./src/directives/MCTTree",
"./src/directives/MCTPreview",
"./src/actions/MCTPreviewAction",
"./src/filters/ReverseFilter",
"text!./res/templates/bottombar.html",
"text!./res/templates/controls/action-button.html",
@@ -69,6 +71,7 @@ define([
"text!./res/templates/controls/selector.html",
"text!./res/templates/controls/datetime-picker.html",
"text!./res/templates/controls/datetime-field.html",
"text!./res/templates/preview.html",
'legacyRegistry'
], function (
UrlService,
@@ -99,6 +102,8 @@ define([
MCTSplitPane,
MCTSplitter,
MCTTree,
MCTPreview,
MCTPreviewAction,
ReverseFilter,
bottombarTemplate,
actionButtonTemplate,
@@ -119,6 +124,7 @@ define([
selectorTemplate,
datetimePickerTemplate,
datetimeFieldTemplate,
previewTemplate,
legacyRegistry
) {
@@ -396,6 +402,31 @@ define([
"key": "mctTree",
"implementation": MCTTree,
"depends": ['gestureService']
},
{
"key": "mctPreview",
"implementation": MCTPreview,
"depends": [
"$document"
]
}
],
"actions": [
{
"key": "mct-preview-action",
"implementation": MCTPreviewAction,
"name": "Preview",
"cssClass": "hide-in-t-main-view icon-eye-open",
"description": "Preview in large dialog",
"category": [
"contextual",
"view-control"
],
"depends": [
"$compile",
"$rootScope"
],
"priority": "preferred"
}
],
"constants": [
@@ -511,6 +542,10 @@ define([
{
"key": "object-inspector",
"template": objectInspectorTemplate
},
{
"key": "mct-preview",
"template": previewTemplate
}
],
"controls": [

View File

@@ -2,7 +2,7 @@
"metadata": {
"name": "openmct-symbols-16px",
"lastOpened": 0,
"created": 1506973656040
"created": 1529545133464
},
"iconSets": [
{
@@ -40,7 +40,7 @@
"tempChar": ""
},
{
"order": 44,
"order": 141,
"prevSize": 24,
"name": "icon-arrow-right",
"id": 39,
@@ -48,7 +48,7 @@
"tempChar": ""
},
{
"order": 41,
"order": 142,
"prevSize": 24,
"name": "icon-arrow-double-up",
"id": 36,
@@ -213,7 +213,7 @@
"tempChar": ""
},
{
"order": 29,
"order": 144,
"prevSize": 24,
"name": "icon-person",
"id": 24,
@@ -221,7 +221,7 @@
"tempChar": ""
},
{
"order": 86,
"order": 147,
"prevSize": 24,
"name": "icon-plus",
"id": 81,
@@ -268,13 +268,21 @@
"code": 59697,
"tempChar": ""
},
{
"order": 148,
"id": 118,
"name": "icon-arrow-right-equilateral",
"prevSize": 24,
"code": 59698,
"tempChar": ""
},
{
"order": 48,
"prevSize": 24,
"name": "icon-arrows-out",
"id": 43,
"code": 921600,
"tempChar": ""
"tempChar": ""
},
{
"order": 49,
@@ -282,7 +290,7 @@
"name": "icon-arrows-right-left",
"id": 44,
"code": 921601,
"tempChar": ""
"tempChar": ""
},
{
"order": 50,
@@ -290,7 +298,7 @@
"name": "icon-arrows-up-down",
"id": 45,
"code": 921602,
"tempChar": ""
"tempChar": ""
},
{
"order": 9,
@@ -298,7 +306,7 @@
"name": "icon-bullet",
"id": 4,
"code": 921604,
"tempChar": ""
"tempChar": ""
},
{
"order": 25,
@@ -306,7 +314,7 @@
"name": "icon-calendar",
"id": 20,
"code": 921605,
"tempChar": ""
"tempChar": ""
},
{
"order": 30,
@@ -314,7 +322,7 @@
"name": "icon-chain-links",
"id": 25,
"code": 921606,
"tempChar": ""
"tempChar": ""
},
{
"order": 109,
@@ -322,7 +330,7 @@
"prevSize": 24,
"code": 921607,
"name": "icon-pane-collapse-left",
"tempChar": ""
"tempChar": ""
},
{
"order": 110,
@@ -330,7 +338,7 @@
"prevSize": 24,
"code": 921608,
"name": "icon-pane-collapse-right",
"tempChar": ""
"tempChar": ""
},
{
"order": 5,
@@ -338,7 +346,7 @@
"prevSize": 24,
"code": 921609,
"name": "icon-download",
"tempChar": ""
"tempChar": ""
},
{
"order": 60,
@@ -346,7 +354,7 @@
"name": "icon-duplicate",
"id": 55,
"code": 921616,
"tempChar": ""
"tempChar": ""
},
{
"order": 61,
@@ -354,7 +362,7 @@
"name": "icon-folder-new",
"id": 56,
"code": 921617,
"tempChar": ""
"tempChar": ""
},
{
"order": 64,
@@ -362,7 +370,7 @@
"name": "icon-fullscreen-expand",
"id": 59,
"code": 921618,
"tempChar": ""
"tempChar": ""
},
{
"order": 63,
@@ -370,7 +378,7 @@
"name": "icon-fullscreen-collapse",
"id": 58,
"code": 921619,
"tempChar": ""
"tempChar": ""
},
{
"order": 67,
@@ -378,23 +386,23 @@
"name": "icon-layers",
"id": 62,
"code": 921620,
"tempChar": ""
"tempChar": ""
},
{
"order": 69,
"order": 145,
"prevSize": 24,
"name": "icon-line-horz",
"id": 64,
"code": 921621,
"tempChar": ""
"tempChar": ""
},
{
"order": 73,
"order": 146,
"prevSize": 24,
"name": "icon-magnify",
"id": 68,
"code": 921622,
"tempChar": ""
"tempChar": ""
},
{
"order": 71,
@@ -402,7 +410,7 @@
"name": "icon-magnify-in",
"id": 66,
"code": 921623,
"tempChar": ""
"tempChar": ""
},
{
"order": 72,
@@ -410,7 +418,7 @@
"name": "icon-magnify-out",
"id": 67,
"code": 921624,
"tempChar": ""
"tempChar": ""
},
{
"order": 74,
@@ -418,7 +426,7 @@
"name": "icon-menu",
"id": 69,
"code": 921625,
"tempChar": ""
"tempChar": ""
},
{
"order": 75,
@@ -426,7 +434,7 @@
"name": "icon-move",
"id": 70,
"code": 921632,
"tempChar": ""
"tempChar": ""
},
{
"order": 76,
@@ -434,7 +442,7 @@
"name": "icon-new-window",
"id": 71,
"code": 921633,
"tempChar": ""
"tempChar": ""
},
{
"order": 26,
@@ -442,7 +450,7 @@
"name": "icon-paint-bucket",
"id": 21,
"code": 921634,
"tempChar": ""
"tempChar": ""
},
{
"order": 81,
@@ -450,7 +458,7 @@
"name": "icon-pause",
"id": 76,
"code": 921635,
"tempChar": ""
"tempChar": ""
},
{
"order": 82,
@@ -458,7 +466,7 @@
"name": "icon-pencil",
"id": 77,
"code": 921636,
"tempChar": ""
"tempChar": ""
},
{
"order": 84,
@@ -466,7 +474,7 @@
"name": "icon-play",
"id": 79,
"code": 921637,
"tempChar": ""
"tempChar": ""
},
{
"order": 85,
@@ -474,7 +482,7 @@
"name": "icon-plot-resource",
"id": 80,
"code": 921638,
"tempChar": ""
"tempChar": ""
},
{
"order": 27,
@@ -482,7 +490,7 @@
"name": "icon-pointer-left",
"id": 22,
"code": 921639,
"tempChar": ""
"tempChar": ""
},
{
"order": 28,
@@ -490,7 +498,7 @@
"name": "icon-pointer-right",
"id": 23,
"code": 921640,
"tempChar": ""
"tempChar": ""
},
{
"order": 32,
@@ -498,7 +506,7 @@
"name": "icon-refresh",
"id": 27,
"code": 921641,
"tempChar": ""
"tempChar": ""
},
{
"order": 16,
@@ -506,7 +514,7 @@
"name": "icon-save",
"id": 11,
"code": 921648,
"tempChar": ""
"tempChar": ""
},
{
"order": 88,
@@ -514,7 +522,7 @@
"name": "icon-sine",
"id": 83,
"code": 921649,
"tempChar": ""
"tempChar": ""
},
{
"order": 102,
@@ -522,7 +530,7 @@
"name": "icon-T",
"id": 84,
"code": 921650,
"tempChar": ""
"tempChar": ""
},
{
"order": 92,
@@ -530,7 +538,7 @@
"name": "icon-thumbs-strip",
"id": 87,
"code": 921651,
"tempChar": ""
"tempChar": ""
},
{
"order": 96,
@@ -538,7 +546,7 @@
"name": "icon-two-parts-both",
"id": 91,
"code": 921652,
"tempChar": ""
"tempChar": ""
},
{
"order": 97,
@@ -546,7 +554,7 @@
"name": "icon-two-parts-one-only",
"id": 92,
"code": 921653,
"tempChar": ""
"tempChar": ""
},
{
"order": 21,
@@ -554,7 +562,7 @@
"name": "icon-resync",
"id": 16,
"code": 921654,
"tempChar": ""
"tempChar": ""
},
{
"order": 120,
@@ -562,7 +570,7 @@
"name": "icon-reset",
"prevSize": 24,
"code": 921655,
"tempChar": ""
"tempChar": ""
},
{
"order": 121,
@@ -570,7 +578,7 @@
"name": "icon-x-in-circle",
"prevSize": 24,
"code": 921656,
"tempChar": ""
"tempChar": ""
},
{
"order": 118,
@@ -578,7 +586,7 @@
"name": "icon-brightness",
"prevSize": 24,
"code": 921657,
"tempChar": ""
"tempChar": ""
},
{
"order": 119,
@@ -586,7 +594,7 @@
"name": "icon-contrast",
"prevSize": 24,
"code": 921664,
"tempChar": ""
"tempChar": ""
},
{
"order": 124,
@@ -594,7 +602,7 @@
"name": "icon-expand",
"prevSize": 24,
"code": 921665,
"tempChar": ""
"tempChar": ""
},
{
"order": 125,
@@ -602,7 +610,7 @@
"name": "icon-list-view",
"prevSize": 24,
"code": 921666,
"tempChar": ""
"tempChar": ""
},
{
"order": 133,
@@ -610,7 +618,7 @@
"name": "icon-grid-snap-to",
"prevSize": 24,
"code": 921667,
"tempChar": ""
"tempChar": ""
},
{
"order": 128,
@@ -618,7 +626,7 @@
"name": "icon-grid-snap-no",
"prevSize": 24,
"code": 921668,
"tempChar": ""
"tempChar": ""
},
{
"order": 131,
@@ -626,7 +634,7 @@
"name": "icon-frame-show",
"prevSize": 24,
"code": 921669,
"tempChar": ""
"tempChar": ""
},
{
"order": 130,
@@ -634,7 +642,7 @@
"name": "icon-frame-hide",
"prevSize": 24,
"code": 921670,
"tempChar": ""
"tempChar": ""
},
{
"order": 138,
@@ -642,7 +650,7 @@
"name": "icon-import",
"prevSize": 24,
"code": 921671,
"tempChar": ""
"tempChar": ""
},
{
"order": 136,
@@ -650,7 +658,7 @@
"name": "icon-export",
"prevSize": 24,
"code": 921672,
"tempChar": ""
"tempChar": ""
},
{
"order": 37,
@@ -658,7 +666,7 @@
"name": "icon-activity",
"id": 32,
"code": 921856,
"tempChar": ""
"tempChar": ""
},
{
"order": 36,
@@ -666,7 +674,7 @@
"name": "icon-activity-mode",
"id": 31,
"code": 921857,
"tempChar": ""
"tempChar": ""
},
{
"order": 52,
@@ -674,7 +682,7 @@
"name": "icon-autoflow-tabular",
"id": 47,
"code": 921858,
"tempChar": ""
"tempChar": ""
},
{
"order": 55,
@@ -682,7 +690,7 @@
"name": "icon-clock",
"id": 50,
"code": 921859,
"tempChar": ""
"tempChar": ""
},
{
"order": 58,
@@ -690,7 +698,7 @@
"name": "icon-database",
"id": 53,
"code": 921860,
"tempChar": ""
"tempChar": ""
},
{
"order": 57,
@@ -698,7 +706,7 @@
"name": "icon-database-query",
"id": 52,
"code": 921861,
"tempChar": ""
"tempChar": ""
},
{
"order": 17,
@@ -706,7 +714,7 @@
"name": "icon-dataset",
"id": 12,
"code": 921862,
"tempChar": ""
"tempChar": ""
},
{
"order": 22,
@@ -714,7 +722,7 @@
"name": "icon-datatable",
"id": 17,
"code": 921863,
"tempChar": ""
"tempChar": ""
},
{
"order": 59,
@@ -722,7 +730,7 @@
"name": "icon-dictionary",
"id": 54,
"code": 921864,
"tempChar": ""
"tempChar": ""
},
{
"order": 62,
@@ -730,7 +738,7 @@
"name": "icon-folder",
"id": 57,
"code": 921865,
"tempChar": ""
"tempChar": ""
},
{
"order": 66,
@@ -738,7 +746,7 @@
"name": "icon-image",
"id": 61,
"code": 921872,
"tempChar": ""
"tempChar": ""
},
{
"order": 68,
@@ -746,7 +754,7 @@
"name": "icon-layout",
"id": 63,
"code": 921873,
"tempChar": ""
"tempChar": ""
},
{
"order": 77,
@@ -754,7 +762,7 @@
"name": "icon-object",
"id": 72,
"code": 921874,
"tempChar": ""
"tempChar": ""
},
{
"order": 78,
@@ -762,7 +770,7 @@
"name": "icon-object-unknown",
"id": 73,
"code": 921875,
"tempChar": ""
"tempChar": ""
},
{
"order": 79,
@@ -770,7 +778,7 @@
"name": "icon-packet",
"id": 74,
"code": 921876,
"tempChar": ""
"tempChar": ""
},
{
"order": 80,
@@ -778,7 +786,7 @@
"name": "icon-page",
"id": 75,
"code": 921877,
"tempChar": ""
"tempChar": ""
},
{
"order": 135,
@@ -786,7 +794,7 @@
"name": "icon-plot-overlay",
"prevSize": 24,
"code": 921878,
"tempChar": ""
"tempChar": ""
},
{
"order": 113,
@@ -794,7 +802,7 @@
"name": "icon-plot-stacked",
"prevSize": 24,
"code": 921879,
"tempChar": ""
"tempChar": ""
},
{
"order": 10,
@@ -802,7 +810,7 @@
"name": "icon-session",
"id": 5,
"code": 921880,
"tempChar": ""
"tempChar": ""
},
{
"order": 24,
@@ -810,7 +818,7 @@
"name": "icon-tabular",
"id": 19,
"code": 921881,
"tempChar": ""
"tempChar": ""
},
{
"order": 7,
@@ -818,7 +826,7 @@
"name": "icon-tabular-lad",
"id": 2,
"code": 921888,
"tempChar": ""
"tempChar": ""
},
{
"order": 6,
@@ -826,7 +834,7 @@
"name": "icon-tabular-lad-set",
"id": 1,
"code": 921889,
"tempChar": ""
"tempChar": ""
},
{
"order": 8,
@@ -834,7 +842,7 @@
"name": "icon-tabular-realtime",
"id": 3,
"code": 921890,
"tempChar": ""
"tempChar": ""
},
{
"order": 23,
@@ -842,7 +850,7 @@
"name": "icon-tabular-scrolling",
"id": 18,
"code": 921891,
"tempChar": ""
"tempChar": ""
},
{
"order": 112,
@@ -850,7 +858,7 @@
"name": "icon-telemetry",
"id": 86,
"code": 921892,
"tempChar": ""
"tempChar": ""
},
{
"order": 90,
@@ -858,7 +866,7 @@
"name": "icon-telemetry-panel",
"id": 85,
"code": 921893,
"tempChar": ""
"tempChar": ""
},
{
"order": 93,
@@ -866,7 +874,7 @@
"name": "icon-timeline",
"id": 88,
"code": 921894,
"tempChar": ""
"tempChar": ""
},
{
"order": 116,
@@ -874,7 +882,7 @@
"name": "icon-timer-v1.5",
"prevSize": 24,
"code": 921895,
"tempChar": ""
"tempChar": ""
},
{
"order": 11,
@@ -882,7 +890,7 @@
"name": "icon-topic",
"id": 6,
"code": 921896,
"tempChar": ""
"tempChar": ""
},
{
"order": 115,
@@ -890,7 +898,7 @@
"name": "icon-box-with-dashed-lines",
"id": 29,
"code": 921897,
"tempChar": ""
"tempChar": ""
},
{
"order": 126,
@@ -898,7 +906,7 @@
"name": "icon-summary-widget",
"prevSize": 24,
"code": 921904,
"tempChar": ""
"tempChar": ""
},
{
"order": 139,
@@ -906,7 +914,7 @@
"name": "icon-notebook",
"prevSize": 24,
"code": 921905,
"tempChar": ""
"tempChar": ""
}
],
"metadata": {
@@ -1639,6 +1647,26 @@
]
}
},
{
"id": 118,
"paths": [
"M962 512l-896 512v-1024z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-arrow-right-equilateral"
],
"colorPermutations": {
"1161751207457516161751": [
{}
]
}
},
{
"paths": [
"M0 512l256 256v-512z",
@@ -1938,7 +1966,7 @@
},
{
"paths": [
"M1024 896l-201.662-201.662c47.922-72.498 73.662-157.434 73.662-246.338 0-119.666-46.6-232.168-131.216-316.784s-197.118-131.216-316.784-131.216-232.168 46.6-316.784 131.216-131.216 197.118-131.216 316.784 46.6 232.168 131.216 316.784 197.118 131.216 316.784 131.216c88.904 0 173.84-25.74 246.338-73.662l201.662 201.662 128-128zM448 704c-141.16 0-256-114.842-256-256 0-141.16 114.84-256 256-256 141.158 0 256 114.84 256 256 0 141.158-114.842 256-256 256z"
"M1024 896l-256.8-256.8c42.4-66.6 65-144 64.8-223.2 0-229.8-186.2-416-416-416s-416 186.2-416 416 186.2 416 416 416c79 0.2 156.4-22.4 223.2-64.8l256.8 256.8 128-128zM212.4 619.6c-112.4-112.4-112.4-294.8 0-407.2s294.8-112.4 407.2 0 112.4 294.8 0 407.2c-54 54-127.2 84.4-203.6 84.4-76.4 0.2-149.8-30.2-203.6-84.4z"
],
"grid": 16,
"tags": [
@@ -1946,15 +1974,26 @@
],
"defaultCode": 77,
"id": 68,
"attrs": [],
"attrs": [
{
"fill": "rgb(0, 161, 75)"
}
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"1161751207457516161751": []
"1161751207457516161751": [
{
"f": 1
}
]
}
},
{
"paths": [
"M640 384h-128v-128h-128v128h-128v128h128v128h128v-128h128z",
"M1024 896l-201.662-201.662c47.922-72.498 73.662-157.434 73.662-246.338 0-119.666-46.6-232.168-131.216-316.784s-197.118-131.216-316.784-131.216c-119.666 0-232.168 46.6-316.784 131.216s-131.216 197.118-131.216 316.784c0 119.666 46.6 232.168 131.216 316.784s197.118 131.216 316.784 131.216c88.904 0 173.84-25.74 246.338-73.662l201.662 201.662 128-128zM448 704c-141.16 0-256-114.842-256-256 0-141.16 114.84-256 256-256 141.158 0 256 114.84 256 256 0 141.158-114.842 256-256 256z"
"M1024 896l-256.86-256.86c40.681-62.963 64.861-139.898 64.861-222.481 0-0.232-0-0.464-0.001-0.696l0 0.036c0-229.76-186.24-416-416-416s-416 186.24-416 416 186.24 416 416 416c0.196 0 0.427 0.001 0.659 0.001 82.583 0 159.518-24.18 224.112-65.846l-1.631 0.985 256.86 256.86zM212.36 619.64c-52.114-52.117-84.346-124.114-84.346-203.64 0-159.058 128.942-288 288-288s288 128.942 288 288c0 159.058-128.942 288-288 288-0.005 0-0.010-0-0.014-0l0.001 0c-0.242 0.001-0.529 0.001-0.815 0.001-79.271 0-151.010-32.251-202.811-84.348l-0.013-0.014z",
"M224 352h384v128h-384v-128z",
"M352 224h128v384h-128v-384z"
],
"grid": 16,
"tags": [
@@ -1962,15 +2001,37 @@
],
"defaultCode": 88,
"id": 66,
"attrs": [],
"attrs": [
{
"fill": "rgb(0, 161, 75)"
},
{
"fill": "rgb(0, 161, 75)"
},
{
"fill": "rgb(0, 161, 75)"
}
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"1161751207457516161751": []
"1161751207457516161751": [
{
"f": 1
},
{
"f": 1
},
{
"f": 1
}
]
}
},
{
"paths": [
"M256 384h384v128h-384v-128z",
"M1024 896l-201.662-201.662c47.922-72.498 73.662-157.434 73.662-246.338 0-119.666-46.6-232.168-131.216-316.784s-197.118-131.216-316.784-131.216c-119.666 0-232.168 46.6-316.784 131.216s-131.216 197.118-131.216 316.784c0 119.666 46.6 232.168 131.216 316.784s197.118 131.216 316.784 131.216c88.904 0 173.84-25.74 246.338-73.662l201.662 201.662 128-128zM448 704c-141.16 0-256-114.842-256-256 0-141.16 114.84-256 256-256 141.158 0 256 114.84 256 256 0 141.158-114.842 256-256 256z"
"M767.2 639.2c42.4-66.6 65-144 64.8-223.2 0-229.8-186.2-416-416-416s-416 186.2-416 416 186.2 416 416 416c79 0.2 156.4-22.4 223.2-64.8l256.8 256.8 128-128-256.8-256.8zM619.6 619.6c-54 54-127.2 84.4-203.6 84.4-76.4 0.2-149.8-30.2-203.6-84.4-112.4-112.4-112.4-294.8 0-407.2s294.8-112.4 407.2 0c112.4 112.4 112.4 294.8 0 407.2z",
"M224 352h384v128h-384v-128z"
],
"grid": 16,
"tags": [
@@ -1978,16 +2039,32 @@
],
"defaultCode": 89,
"id": 67,
"attrs": [],
"attrs": [
{
"fill": "rgb(0, 161, 75)"
},
{
"fill": "rgb(0, 161, 75)"
}
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"1161751207457516161751": []
"1161751207457516161751": [
{
"f": 1
},
{
"f": 1
}
]
}
},
{
"paths": [
"M0 0h1024v256h-1024v-256z",
"M0 384h1024v256h-1024v-256z",
"M0 768h1024v256h-1024v-256z"
"M0 128h1024v128h-1024v-128z",
"M0 448h1024v128h-1024v-128z",
"M0 768h1024v128h-1024v-128z"
],
"grid": 16,
"tags": [
@@ -1995,9 +2072,19 @@
],
"defaultCode": 109,
"id": 69,
"attrs": [],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"1161751207457516161751": []
"1161751207457516161751": [
{},
{},
{}
]
}
},
{

View File

@@ -39,6 +39,7 @@
<glyph unicode="&#xe929;" glyph-name="icon-brackets" d="M832 960h-192v-192h191.66l0.34-0.34v-639.32l-0.34-0.34h-191.66v-192h192c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM384 128h-191.66l-0.34 0.34v639.32l0.34 0.34h191.66v192h-192c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h192v192z" />
<glyph unicode="&#xe930;" glyph-name="icon-crosshair" d="M574 962h-128v-320h128v320zM1022 514h-320v-128h320v128zM574 258h-128v-320h128v320zM318 514h-320v-128h320v128z" />
<glyph unicode="&#xe931;" glyph-name="icon-grippy-v2" horiz-adv-x="586" d="M146.4 777.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM146.4 557.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM146.4 338.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM146.4 118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 886.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 667.4c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 448c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 228.6c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.8 9.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 777.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 557.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 338.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM585.2 118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2z" />
<glyph unicode="&#xe932;" glyph-name="icon-arrow-right-equilateral" d="M962 448l-896-512v1024z" />
<glyph unicode="&#xe1000;" glyph-name="icon-arrows-out" d="M0 448l256-256v512zM512 960l-256-256h512zM512-64l256 256h-512zM768 704v-512l256 256z" />
<glyph unicode="&#xe1001;" glyph-name="icon-arrows-right-left" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
<glyph unicode="&#xe1002;" glyph-name="icon-arrows-up-down" d="M512 960l512-448h-1024zM0 384l512-448 512 448z" />
@@ -54,10 +55,10 @@
<glyph unicode="&#xe1013;" glyph-name="icon-fullscreen-collapse" d="M191.656 128c0.118-0.1 0.244-0.224 0.344-0.344v-191.656h192v192c0 105.6-86.4 192-192 192h-192v-192h191.656zM192 768.344c-0.1-0.118-0.224-0.244-0.344-0.344h-191.656v-192h192c105.6 0 192 86.4 192 192v192h-192v-191.656zM832 576h192v192h-191.656c-0.118 0.1-0.244 0.226-0.344 0.344v191.656h-192v-192c0-105.6 86.4-192 192-192zM832 127.656c0.1 0.118 0.224 0.244 0.344 0.344h191.656v192h-192c-105.6 0-192-86.4-192-192v-192h192v191.656z" />
<glyph unicode="&#xe1014;" glyph-name="icon-layers" d="M1024 576l-512 384-512-384 512-384zM512 64l-426.666 320-85.334-64 512-384 512 384-85.334 64z" />
<glyph unicode="&#xe1015;" glyph-name="icon-line-horz" d="M64 384c-35.346 0-64 28.654-64 64s28.654 64 64 64h896c35.346 0 64-28.654 64-64s-28.654-64-64-64h-896z" />
<glyph unicode="&#xe1016;" glyph-name="icon-magnify" d="M1024 64l-201.662 201.662c47.922 72.498 73.662 157.434 73.662 246.338 0 119.666-46.6 232.168-131.216 316.784s-197.118 131.216-316.784 131.216-232.168-46.6-316.784-131.216-131.216-197.118-131.216-316.784 46.6-232.168 131.216-316.784 197.118-131.216 316.784-131.216c88.904 0 173.84 25.74 246.338 73.662l201.662-201.662 128 128zM448 256c-141.16 0-256 114.842-256 256 0 141.16 114.84 256 256 256 141.158 0 256-114.84 256-256 0-141.158-114.842-256-256-256z" />
<glyph unicode="&#xe1017;" glyph-name="icon-magnify-in" d="M640 576h-128v128h-128v-128h-128v-128h128v-128h128v128h128zM1024 64l-201.662 201.662c47.922 72.498 73.662 157.434 73.662 246.338 0 119.666-46.6 232.168-131.216 316.784s-197.118 131.216-316.784 131.216c-119.666 0-232.168-46.6-316.784-131.216s-131.216-197.118-131.216-316.784c0-119.666 46.6-232.168 131.216-316.784s197.118-131.216 316.784-131.216c88.904 0 173.84 25.74 246.338 73.662l201.662-201.662 128 128zM448 256c-141.16 0-256 114.842-256 256 0 141.16 114.84 256 256 256 141.158 0 256-114.84 256-256 0-141.158-114.842-256-256-256z" />
<glyph unicode="&#xe1018;" glyph-name="icon-magnify-out" d="M256 576h384v-128h-384v128zM1024 64l-201.662 201.662c47.922 72.498 73.662 157.434 73.662 246.338 0 119.666-46.6 232.168-131.216 316.784s-197.118 131.216-316.784 131.216c-119.666 0-232.168-46.6-316.784-131.216s-131.216-197.118-131.216-316.784c0-119.666 46.6-232.168 131.216-316.784s197.118-131.216 316.784-131.216c88.904 0 173.84 25.74 246.338 73.662l201.662-201.662 128 128zM448 256c-141.16 0-256 114.842-256 256 0 141.16 114.84 256 256 256 141.158 0 256-114.84 256-256 0-141.158-114.842-256-256-256z" />
<glyph unicode="&#xe1019;" glyph-name="icon-menu" d="M0 960h1024v-256h-1024v256zM0 576h1024v-256h-1024v256zM0 192h1024v-256h-1024v256z" />
<glyph unicode="&#xe1016;" glyph-name="icon-magnify" d="M1024 64l-256.8 256.8c42.4 66.6 65 144 64.8 223.2 0 229.8-186.2 416-416 416s-416-186.2-416-416 186.2-416 416-416c79-0.2 156.4 22.4 223.2 64.8l256.8-256.8 128 128zM212.4 340.4c-112.4 112.4-112.4 294.8 0 407.2s294.8 112.4 407.2 0 112.4-294.8 0-407.2c-54-54-127.2-84.4-203.6-84.4-76.4-0.2-149.8 30.2-203.6 84.4z" />
<glyph unicode="&#xe1017;" glyph-name="icon-magnify-in" d="M1024 64l-256.86 256.86c40.681 62.963 64.861 139.898 64.861 222.481 0 0.232 0 0.464-0.001 0.696v-0.036c0 229.76-186.24 416-416 416s-416-186.24-416-416 186.24-416 416-416c0.196 0 0.427-0.001 0.659-0.001 82.583 0 159.518 24.18 224.112 65.846l-1.631-0.985 256.86-256.86zM212.36 340.36c-52.114 52.117-84.346 124.114-84.346 203.64 0 159.058 128.942 288 288 288s288-128.942 288-288c0-159.058-128.942-288-288-288-0.005 0-0.010 0-0.014 0h0.001c-0.242-0.001-0.529-0.001-0.815-0.001-79.271 0-151.010 32.251-202.811 84.348l-0.013 0.014zM224 608h384v-128h-384v128zM352 736h128v-384h-128v384z" />
<glyph unicode="&#xe1018;" glyph-name="icon-magnify-out" d="M767.2 320.8c42.4 66.6 65 144 64.8 223.2 0 229.8-186.2 416-416 416s-416-186.2-416-416 186.2-416 416-416c79-0.2 156.4 22.4 223.2 64.8l256.8-256.8 128 128-256.8 256.8zM619.6 340.4c-54-54-127.2-84.4-203.6-84.4-76.4-0.2-149.8 30.2-203.6 84.4-112.4 112.4-112.4 294.8 0 407.2s294.8 112.4 407.2 0c112.4-112.4 112.4-294.8 0-407.2zM224 608h384v-128h-384v128z" />
<glyph unicode="&#xe1019;" glyph-name="icon-menu" d="M0 832h1024v-128h-1024v128zM0 512h1024v-128h-1024v128zM0 192h1024v-128h-1024v128z" />
<glyph unicode="&#xe1020;" glyph-name="icon-move" d="M293.4 448l218.6 218.6 256-256v421.4c0 70.4-57.6 128-128 128h-512c-70.4 0-128-57.6-128-128v-512c0-70.4 57.6-128 128-128h421.4l-256 256zM1024 512h-128v-320l-384 384-128-128 384-384h-320v-128h576z" />
<glyph unicode="&#xe1021;" glyph-name="icon-new-window" d="M448 960v-128h320l-384-384 128-128 384 384v-320h128v576zM576 285.726v-157.382c-0.1-0.118-0.226-0.244-0.344-0.344h-383.312c-0.118 0.1-0.244 0.226-0.344 0.344v383.312c0.1 0.118 0.226 0.244 0.344 0.344h157.382l192 192h-349.726c-105.6 0-192-86.4-192-192v-384c0-105.6 86.4-192 192-192h384c105.6 0 192 86.4 192 192v349.726l-192-192z" />
<glyph unicode="&#xe1022;" glyph-name="icon-paint-bucket" d="M544 736v-224c0-88.4-71.6-160-160-160s-160 71.6-160 160v97.2l-197.4-196.4c-50-50-12.4-215.2 112.4-340s290-162.4 340-112.4l417 423.6-352 352zM896-64c70.6 0 128 57.4 128 128 0 108.6-128 192-128 192s-128-83.4-128-192c0-70.6 57.4-128 128-128zM384 448c-35.4 0-64 28.6-64 64v384c0 35.4 28.6 64 64 64s64-28.6 64-64v-384c0-35.4-28.6-64-64-64z" />

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -157,3 +157,57 @@
.t-popup {
z-index: 75;
}
/********************************************* GRID STYLES */
.grid-two-column {
display: grid;
grid-row-gap: 0;
grid-template-columns: 1fr 2fr;
align-items: top;
}
.grid-two-column-span-cols {
grid-column: 1 / 3;
}
.grid-elem {
&:not(:first-child) {
border-top: 1px solid $colorInteriorBorder;
}
&.label {
background-color: rgba(0,0,128,0.2);
}
&.value {
background-color: rgba(0,128,0,0.2);
}
}
// Properties grids
.grid-properties {
@extend .grid-two-column;
}
.grid-row {
display: contents;
}
.grid-span-all {
@extend .grid-two-column-span-cols;
}
.grid-row {
.grid-cell {
padding: 3px $interiorMarginLg 3px 0;
&[title] {
// When a cell has a title, assume it's helpful text
cursor: help;
}
}
&.force-border,
&:not(:first-of-type) {
// Row borders, effected via border-top on child elements of the row
.grid-cell {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
}
}

View File

@@ -37,25 +37,9 @@
vertical-align: middle;
}
//.top-bar .btn-browse .badge {
// Moved to _controls.scss .btn.browse-btn
// border-radius: $controlCr * 1.5;
// $d: 20px;
// display: block;
// font-size: 1em;
// line-height: $d;
//// margin-top: -4px;
//
// position: absolute;
// top: 5px; left: 5px; bottom: 5px; right: auto;
// width: $d; height: auto;
//}
.super-menu .badge {
@include background-image(linear-gradient(lighten($colorCreateBtn, 10%), $colorCreateBtn));
border-radius: $controlCr;
@include boxShdwSubtle();
// display: inline-block;
// margin-right: 10px !important;
padding: 2px 7px;
}

View File

@@ -43,10 +43,10 @@ $ueColMargin: 1.5%;
$ueAppLogoW: 80px;
$ueEditToolBarH: 25px;
$ueCollapsedPaneEdgeM: 22px;
$uePaneMiniTabH: 22px;
$uePaneMiniTabW: 8px;
$uePaneMiniTabFontSize: 8px;
$uePaneMiniTabCollapsedW: 18px;
$uePaneMiniTabH: 36px;
$uePaneMiniTabW: 10px;
$uePaneMiniTabFontSize: 9px;
$uePaneMiniTabCollapsedW: 22px;
$ueEditLeftPaneW: 75%;
$treeSearchInputBarH: 25px;
/*************** Panes */
@@ -70,7 +70,7 @@ $ueBrowseGridItemTopBarH: 20px;
$ueBrowseGridItemBottomBarH: 30px;
$itemPadLR: 5px;
/*************** Tree */
$treeVCW: 10px;
$treeVCW: 16px;
$treeTypeIconH: 1.4em; // was 16px
$treeTypeIconHPx: 16px;
$treeTypeIconW: 18px;
@@ -132,7 +132,7 @@ $menuLineHPx: 24px;
$btnStdH: 25px;
$btnToolbarH: $btnStdH;
$controlBarH: $btnStdH;
$btnFrameH: 16px;
$btnFrameH: 18px;
/************************** PATHS */
// Paths need to be relative to /platform/commonUI/theme/<theme-name>/css/ directory

View File

@@ -141,7 +141,6 @@ a.disabled {
.s-status-missing {
// Labels. Expects .s-status-missing to be applied to mct-representation that contains
// .t-object-label
.t-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
@@ -201,6 +200,9 @@ a.disabled {
.vscroll {
overflow-y: auto;
&.scroll-pad {
padding-right: $interiorMargin;
}
}
.slidable {
@@ -224,8 +226,13 @@ a.disabled {
box-shadow: rgba(#000, 0.7) 0 4px 10px 2px;
}
.capitalize {
text-transform: capitalize;
}
.hide,
.hidden {
.hidden,
.t-main-view .hide-in-t-main-view {
display: none !important;
}
@@ -234,14 +241,16 @@ a.disabled {
pointer-events: none;
}
.off {
.invisible {
display: block;
visibility: hidden;
opacity: 0;
height: 0;
margin: 0;
padding: 0;
border: 0;
margin: 0 !important;
transform: scale(0);
pointer-events: none;
position: absolute;
}
.sep {

View File

@@ -33,6 +33,14 @@
}
}
[class*="icon-"].labeled {
// Moved from .s-button and generalized
&:before {
// Fend off label from icon when it's included
margin-right: $interiorMarginSm;
}
}
/************************** CHAR UNICODES */
$glyph-icon-alert-rect: '\e900';
@@ -67,6 +75,7 @@ $glyph-icon-x: '\e928';
$glyph-icon-brackets: '\e929';
$glyph-icon-crosshair: '\e930';
$glyph-icon-grippy: '\e931';
$glyph-icon-arrow-right-equilateral: '\e932';
$glyph-icon-arrows-out: '\e1000';
$glyph-icon-arrows-right-left: '\e1001';
$glyph-icon-arrows-up-down: '\e1002';
@@ -182,6 +191,7 @@ $glyph-icon-notebook: '\e1131';
.icon-brackets { @include glyphBefore($glyph-icon-brackets); }
.icon-crosshair { @include glyphBefore($glyph-icon-crosshair); }
.icon-grippy { @include glyphBefore($glyph-icon-grippy); }
.icon-arrow-right-equilateral { @include glyphBefore($glyph-icon-arrow-right-equilateral); }
.icon-arrows-out { @include glyphBefore($glyph-icon-arrows-out); }
.icon-arrows-right-left { @include glyphBefore($glyph-icon-arrows-right-left); }
.icon-arrows-up-down { @include glyphBefore($glyph-icon-arrows-up-down); }

View File

@@ -19,23 +19,15 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* Styles for the Inspector pane */
.l-inspect,
.l-inspect table tr td {
font-size: 0.75rem;
}
/* New grid layout for the Inspector pane */
.l-inspect {
// Overall layout
@extend .abs;
background: $colorInspectorBg;
color: $colorInspectorFg;
line-height: 140%;
.flex-elem.holder:not(:last-child) {
margin-bottom: $interiorMargin;
}
.pane-header {
color: pushBack($colorInspectorFg, 20%);
font-size: 0.8rem;
@@ -50,80 +42,48 @@
}
}
}
.l-inspector-part {
box-sizing: border-box;
padding-right: $interiorMargin;
.tree .form {
display: contents;
}
.tree {
.grid-properties {
margin-left: $treeVCW + $interiorMarginLg;
}
.section-header {
background: none;
color: $colorInspectorPropName;
border-radius: unset;
font-size: inherit;
padding: $interiorMarginSm 0;
}
mct-form:not(:last-child) .form {
border-bottom: 1px solid $colorInspectorSectionHeaderBg;
}
.form {
margin-bottom: $interiorMarginSm;
padding-bottom: $interiorMarginLg;
.l-section-body {
margin-bottom: 0;
&:not(.first) {
border-top: 1px solid $colorFormLines;
}
}
.form-row {
align-items: center;
border: none !important;
margin-bottom: 0 !important;
padding: $interiorMarginSm 0;
.label {
min-width: 80px;
}
input[type='text'],
input[type='search'] {
width: 100%;
}
}
}
}
ul li,
em.t-inspector-part-header {
display: block;
position: relative;
}
ul li {
margin-bottom: $interiorMarginLg;
}
em.t-inspector-part-header {
border-radius: $basicCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
margin-bottom: $interiorMargin;
padding: floor($formTBPad * .75) $formLRPad;
text-transform: uppercase;
}
.inspector-properties {
&:not(.first) {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
padding: $interiorMarginSm 0;
.grid-properties {
.label {
color: $colorInspectorPropName;
text-transform: uppercase;
}
.value {
color: $colorInspectorPropVal;
word-break: break-all;
&:first-child {
// If there is no preceding .label element, make value span columns
@extend .grid-two-column-span-cols;
}
}
}
.inspector-location {
display: inline-block;
}
h2 {
// Headers for .l-inspector-part elements
@extend .grid-two-column-span-cols;
border-radius: $controlCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
font-size: 0.8rem;
font-weight: normal;
margin: $interiorMarginLg 0 $interiorMarginSm 0;
padding: floor($formTBPad * .75) $formLRPad;
text-transform: uppercase;
&.first {
margin-top: 0;
}
}
@@ -162,6 +122,34 @@
width: 4px;
}
}
// Elements pool
.holder-elements {
.current-elements {
position: relative;
.tree-item {
.t-object-label {
// Elements pool is a flat list, so don't indent items.
font-size: 0.75rem;
left: 0;
}
}
}
}
}
/* Styles for the Inspector pane */
.l-inspect,
.l-inspect table tr td {
font-size: 0.75rem;
}
.l-inspect {
.flex-elem.holder:not(:last-child) {
margin-bottom: $interiorMargin;
}
.holder-elements {
.current-elements {
position: relative;

View File

@@ -299,7 +299,7 @@
color: $ic;
}
@if $bgHov != none {
&:not(.disabled):hover {
&:not([disabled="true"]):not(.disabled):hover {
background: $bgHov;
color: $fgHov;
>.icon,

View File

@@ -270,37 +270,4 @@
@extend .s-summary-widget;
@extend .l-summary-widget;
padding: $interiorMarginSm $interiorMargin;
}
// Hide and show elements in the rule-header on hover
.l-widget-rule,
.l-widget-test-data-item {
.grippy,
.l-rule-action-buttons-wrapper,
.l-condition-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 500ms);
opacity: 0;
}
&:hover {
.grippy,
.l-rule-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
.l-rule-action-buttons-wrapper {
.t-delete {
margin-left: 10px;
}
}
.t-condition {
&:hover {
.l-condition-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
}
}

View File

@@ -34,11 +34,6 @@ $pad: $interiorMargin * $baseRatio;
line-height: $btnStdH;
padding: 0 $pad;
&.labeled:before {
// Icon when it's included
margin-right: $interiorMarginSm;
}
&.lg {
font-size: 1rem;
}
@@ -59,6 +54,10 @@ $pad: $interiorMargin * $baseRatio;
.label, .title-label { display: none; }
}
&[disabled="true"] {
opacity: 0.3;
}
&.pause-play {
@extend .icon-pause;
&.paused {
@@ -114,10 +113,11 @@ body.desktop .mini-tab-icon {
.l-btn-set {
// Buttons that have a very tight conceptual grouping - no internal space between them.
font-size: 0; // Remove space between s-button elements due to white space in markup
white-space: nowrap;
.s-button {
border-radius: 0;
margin-left: 1px;
margin-left: 1px !important;
}
> .s-button {

View File

@@ -128,26 +128,78 @@
}
}
.l-local-controls {
// Control shown when hovering over an object, like plots and imagery
// Default position is upper right
$p: $interiorMargin;
position: absolute;
top: $p; right: $p; bottom: auto;
text-align: right;
z-index: 5;
/******************************************************** LOCAL CONTROLS */
// Controls placed in proximity to or overlaid on components and views
.local-controls-persist {
}
.s-local-controls {
@include trans-prop-nice(opacity);
font-size: 0.7rem;
&.s-wrapper-transluc {
// Semi-opaque wrapper to visually distinguish a control
// from the background
background: rgba($colorTransLucBg, 0.7);
box-sizing: border-box;
border-radius: $controlCr;
.local-controls-hidden {
// Used within .has-local-controls, hidden by default
}
.local-controls-flyout {
}
body.desktop .has-local-controls {
// Helper class, provides hover ability to show local controls
&:hover .local-controls-hidden {
@include trans-prop-nice($props: opacity, $dur: 50ms);
opacity: 1;
pointer-events: inherit;
}
.local-controls-hidden {
@include trans-prop-nice($props: opacity, $dur: 1000ms);
opacity: 0;
pointer-events: none;
}
}
.h-local-controls {
// An explicit outer holder for controls. Typically placed in upper right.
align-items: center;
font-size: 0.7rem;
display: flex;
flex-direction: row;
justify-content: flex-end;
&.h-local-controls-overlay-content {
$p: $interiorMargin;
position: absolute;
top: $p; right: $p;
z-index: 2;
}
.l-btn-set,
.s-button {
&:not(:first-child) {
margin-left: $interiorMargin;
}
}
}
.h-local-controls-overlay-content {
background: $colorBodyBg;
border-radius: $controlCr + 1;
box-sizing: border-box;
border-radius: $controlCr;
padding: 1px;
.s-button {
background: $colorBtnBg;
}
}
.h-local-controls-trans {
background: rgba($colorTransLucBg, 0.5);
padding: $interiorMargin;
}
/******************************************************** VIEW CONTROLS */
@@ -162,7 +214,7 @@
&:before {
position: absolute;
@include trans-prop-nice(transform, 100ms);
content: $glyph-icon-arrow-right;
content: $glyph-icon-arrow-right-equilateral;
@include transform-origin(center);
}
&.expanded:before {
@@ -338,6 +390,7 @@ input[type="text"].s-input-inline,
@include btnSubtle($bg: $colorSelectBg);
@extend .icon-arrow-down; // Context arrow
display: inline-block;
flex: 0 0 auto; // When used in a flex context, controls need to hold their width
padding: 0 $interiorMargin;
overflow: hidden;
position: relative;
@@ -760,13 +813,17 @@ textarea {
@extend .icon-arrow-right;
cursor: pointer;
font-size: 0.75em;
width: $treeVCW;
&:before {
// Arrow glyph
left: 50%;
position: absolute;
@include trans-prop-nice(transform, 100ms);
transform: translateX(-50%);
transform-origin: center;
}
&.expanded:before {
transform: rotate(90deg);
transform: translateX(-50%) rotate(90deg);
}
}

View File

@@ -11,7 +11,6 @@
}
min-width: 150px;
.l-image-main {
background-color: $colorPlotBg;
margin-bottom: $interiorMargin;
}
.l-image-main-controlbar {
@@ -76,6 +75,7 @@
}
.s-image-main {
background-color: $colorPlotBg;
border: 1px solid transparent;
&.paused {
@extend .s-unsynced;
@@ -138,54 +138,51 @@
}
/*************************************** LOCAL CONTROLS */
.l-local-controls {
max-width: 200px;
min-width: 100px;
width: 35%;
input[type="range"] {
display: block;
width: 100%;
&:not(:first-child) {
margin-top: $interiorMarginLg;
}
&:before {
margin-right: $interiorMarginSm;
}
}
.t-reset-btn-holder {
$bc: $scrollbarTrackColorBg;
&:before,
&:after {
border-right: 1px solid $bc;
content:'';
display: block;
width: 5px;
height: 4px;
}
&:before {
border-top: 1px solid $bc;
margin-bottom: 2px;
}
&:after {
border-bottom: 1px solid $bc;
margin-top: 2px;
}
}
&.s-wrapper-transluc {
left: auto;
padding: $interiorMargin $interiorMarginLg;
}
&.l-flex-row {
.t-imagery {
.h-local-controls.h-local-controls-overlay-content {
max-width: 200px;
min-width: 100px;
width: 35%;
align-items: center;
}
padding: $interiorMargin $interiorMarginLg;
input[type="range"] {
display: block;
width: 100%;
&:not(:first-child) {
margin-top: $interiorMarginLg;
}
&:before {
margin-right: $interiorMarginSm;
}
}
.t-reset-btn-holder {
$bc: $scrollbarTrackColorBg;
&:before,
&:after {
border-right: 1px solid $bc;
content:'';
display: block;
width: 5px;
height: 4px;
}
&:before {
border-top: 1px solid $bc;
margin-bottom: 2px;
}
&:after {
border-bottom: 1px solid $bc;
margin-top: 2px;
}
}
}
}
/*************************************** WHEN IN FRAME */
.frame .t-imagery {
.l-image-main-wrapper {

View File

@@ -41,6 +41,7 @@
width: 100%;
.l-form-section {
margin-bottom: $interiorMarginLg * 2;
position: relative;
&.grows {
.l-section-body,
@@ -57,7 +58,6 @@
$m: $interiorMargin;
box-sizing: border-box;
border-top: 1px solid $colorFormLines;
margin-bottom: $interiorMarginLg * 2;
padding: $formTBPad 0;
position: relative;
@@ -132,6 +132,7 @@
width: auto;
order: 2;
}
>.control,
>.controls {
flex: 0 0 auto;
margin-right: $interiorMargin;
@@ -258,19 +259,19 @@
}
}
}
}
.form-error {
// Block element that visually flags an error and contains a message
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
border-radius: $basicCr;
display: block;
padding: 1px 6px;
&:before {
content: $glyph-icon-alert-triangle;
display: inline;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
.form-error {
// Block element that visually flags an error and contains a message
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
border-radius: $basicCr;
display: block;
padding: 1px 6px;
&:before {
content: $glyph-icon-alert-triangle;
display: inline-block;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
}

View File

@@ -87,7 +87,6 @@
$iconD: $formInputH - ($iconEdgeM * 2);
@extend .icon-magnify;
// Adds a magnifying glass before, holds an input and a clear button
display: inline-block;
position: relative;
input[type="search"] {
@@ -97,6 +96,7 @@
.menu-icon,
&:before {
box-sizing: border-box;
color: $colorInputIcon;
display: inline-block;
line-height: inherit;
position: absolute;
@@ -107,11 +107,16 @@
&:before {
// Magnify glass icon
opacity: 0.7;
left: $interiorMargin;
@include trans-prop-nice(color, 250ms);
@include trans-prop-nice(opacity, 250ms);
pointer-events: none;
}
&:hover:before {
opacity: 1;
}
.clear-icon {
right: $iconEdgeM;
// Icon is visible only when there is text input

View File

@@ -118,7 +118,8 @@ table {
tbody, .tbody {
top: $tabularHeaderH * 2;
}
input[type="text"] {
input[type="text"],
input[type="search"] {
box-sizing: border-box;
width: 100%;
}

View File

@@ -71,8 +71,8 @@ body.mobile {
.pane.left.treeview {
@include trans-prop-nice(opacity, 250ms, $delay: 250ms);
@include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%));
width: $proporMenuWithView !important;
right: auto !important;
width: $proporMenuWithView !important;
}
// Sets the right representation when the tree is shown.
.pane.right.items {
@@ -91,7 +91,7 @@ body.mobile {
.object-browse-bar {
&.t-primary {
margin-left: 45px;
margin-left: 30px;
.title-label {
// Prevent inline editing of the object title in the main view in mobile
@@ -136,17 +136,16 @@ body.mobile {
}
}
@media only screen and (max-device-width: $phoMaxW) and (orientation: portrait) {
body.mobile {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right.items {
transform: translateX($proporMenuOnly);
.holder-object-and-inspector {
opacity: 0;
}
body.phone.portrait {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right.items {
left: 0 !important;
transform: translateX($proporMenuOnly);
.holder-object-and-inspector {
opacity: 0;
}
}
}

View File

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

View File

@@ -246,21 +246,6 @@
}
}
.gl-plot-display-area,
.plot-display-area,
.gl-plot-axis-area {
.gl-plot-local-controls,
.l-local-controls {
@include trans-prop-nice(opacity, 150ms);
opacity: 0;
pointer-events: none;
}
&:hover .gl-plot-local-controls,
&:hover .l-local-controls {
opacity: 1;
pointer-events: inherit;
}
}
.gl-plot-display-area,
.plot-display-area {

View File

@@ -26,107 +26,100 @@
@include trans-prop-nice((opacity, color), 150ms);
}
.c-search-btn-wrapper,
.c-search {
display: flex;
flex-flow: row nowrap;
align-items: center;
position: relative;
> * {
&:not(:first-child) { margin-left: $interiorMargin; }
}
}
.c-search-btn-wrapper {
// Holds c-search and Cancel button
//@include test();
&.holder {
margin-bottom: $interiorMargin;
}
}
.c-search {
// New approach to search and filter inputs
// Block element
// Holds magnify glass icon, html input, cancel button, etc.
$m: $interiorMarginSm;
@include nice-input();
flex: 1 1 99%;
font-size: 0.8rem;
height: $btnStdH;
padding: 1px $interiorMargin;
> * {
display: inline-block;
flex: 0 0 auto;
position: relative;
}
&:before {
// Magnify glass icon
content: $glyph-icon-magnify;
font-family: symbolsfont;
opacity: 0.3;
pointer-events: none;
transition: opacity 500ms;
}
&:hover:before {
opacity: 0.6;
}
}
.c-search__clear-input {
// Icon is visible only when there is text input
visibility: hidden;
opacity: 0;
&.show {
visibility: visible;
opacity: 1;
}
&:hover {
opacity: 1;
}
}
input.c-search__search-input {
background: none !important;
box-shadow: none !important; // !important needed to override default for [input]
flex: 1 1 99%;
min-width: 10px;
}
.c-search__search-menu-holder {
left: 100%;
position: absolute;
transform: translate(-30px, 10px);
z-index: 70;
}
.holder-search {
$iconWidth: 20px;
.search-bar {
$textInputHeight: 19px; // This is equal to the default value, 19px
$iconEdgeM: 4px;
$iconD: $treeSearchInputBarH - ($iconEdgeM*2);
@extend .icon-magnify;
font-size: 0.8em;
position: relative;
.search-input {
height: $treeSearchInputBarH;
line-height: $treeSearchInputBarH;
}
&:before,
.clear-input,
.menu-icon {
// :before is magnify glass icon
box-sizing: border-box;
color: $colorInputIcon;
height: $iconD;
width: $iconD;
line-height: $iconD;
position: absolute;
text-align: center;
top: $iconEdgeM;
}
.search-input {
position: relative;
width: 100%;
padding-left: $iconD + $interiorMargin !important;
padding-right: ($iconD * 2) + ($interiorMargin * 2) !important;
// Make work for mct-control textfield
input {
width: inherit; // was 100%
}
}
&:before {
// Magnify glass icon
left: $interiorMarginSm;
@include trans-prop-nice(color, 250ms);
pointer-events: none;
z-index: 1;
}
// Make icon lighten when hovering over search bar
&:hover:before {
color: pullForward($colorInputIcon, 10%);
}
.clear-input {
// Hiding for now with addition of Cancel button
right: $iconD + $interiorMargin;
// Icon is visible only when there is text input
visibility: hidden;
opacity: 0;
&.show {
visibility: visible;
opacity: 1;
}
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
.menu-icon {
// 'v' invoke menu icon
font-size: 0.8em;
padding-right: $iconEdgeM;
right: $iconEdgeM;
text-align: right;
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
.search-menu-holder {
float: right;
left: -20px;
z-index: 70;
transition: visibility .05s, opacity .05s;
}
}
.results-msg {
font-size: 0.8rem;
opacity: 0.6;
}
.active-filter-display {
$s: 0.7em;
$s: 0.7rem;
$p: $interiorMargin;
box-sizing: border-box;
line-height: 130%;
padding-left: $s * 2;
padding-left: $s * 1.5;
font-size: $s;
.clear-filters {

View File

@@ -64,14 +64,16 @@
left: $m;
}
&.frame-template {
.s-button,
.s-menu-button {
height: $ohH;
line-height: $ohH;
padding: 0 $interiorMargin;
> span,
&:before {
font-size: 0.65rem;
.h-local-controls {
.s-button,
.s-menu-button {
height: $btnFrameH;
line-height: $btnFrameH;
padding: 0 $interiorMargin;
> span,
&:before {
font-size: 0.65rem;
}
}
}
@@ -96,8 +98,6 @@
border-color: transparent;
.object-browse-bar .right {
$m: 0;
background: rgba(black, 0.3);
border-radius: $basicCr;
padding: $interiorMarginSm;
position: absolute;
top: $m; right: $m;
@@ -111,10 +111,23 @@
bottom: $m;
left: $m;
}
> .t-frame-inner {
> .object-browse-bar .left {
display: none;
&.t-object-type-layout {
> .object-browse-bar {
.t-btn-view-large {
// When a nested layout has its frame hidden, don't display a view large button
display: none;
}
}
}
> .object-browse-bar {
.left {
display: none;
}
}
> .object-holder.abs {
overflow: hidden;
top: 0 !important;
@@ -123,8 +136,8 @@
}
}
&.t-frame-outer .s-input-inline {
// Prevent inline inputs from being edited when nested in a Layout
&.t-frame-outer .title-label.s-input-inline {
// Prevent frame titles from being edited when nested in a Layout
pointer-events: none !important;
}
@@ -161,6 +174,7 @@ body.desktop .frame {
// Hide local controls initially and show it them on hover when they're in an element that's in a frame context
// Frame template is used because we need to target the lowest nested frame
.object-browse-bar .btn-bar {
@include trans-prop-nice($props: opacity, $dur: 250ms);
opacity: 0;
pointer-events: none;
}
@@ -169,6 +183,7 @@ body.desktop .frame {
// Handles the case where we have layouts in layouts.
&:hover > .object-browse-bar {
.btn-bar {
@include trans-prop-nice($props: opacity, $dur: 10ms);
opacity: 1;
pointer-events: inherit;
}

View File

@@ -145,20 +145,18 @@
body.desktop .pane .mini-tab-icon.toggle-pane {
$hi: splitterHandleInset($splitterD, $splitterHandleD);
$paneExpandedOffset: $hi + $splitterHandleD + $uePaneMiniTabW;
top: $bodyMargin + ($ueTopBarH - $uePaneMiniTabH)/2;
top: 0;
&.toggle-tree.anchor-left.collapsed,
&.toggle-inspect.anchor-right:not(.collapsed) {
@extend .icon-arrow-right;
border-bottom-right-radius: $controlCr;
border-top-right-radius: $controlCr;
}
&.toggle-tree.anchor-left:not(.collapsed),
&.toggle-inspect.anchor-right.collapsed {
@extend .icon-arrow-left;
border-bottom-left-radius: $controlCr;
border-top-left-radius: $controlCr;
}
&.toggle-tree.anchor-left {
@@ -240,7 +238,6 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
}
}
.object-browse-bar .s-button,
.top-bar .buttons-main .s-button,
.top-bar .s-menu-button,
.tool-bar .s-button,
@@ -268,7 +265,12 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
.left {
.l-back {
margin-right: $interiorMarginLg;
a {
display: inline-block;
text-align: center;
width: 24px;
}
margin-right: $interiorMargin;
&.s-status-editing { display: none; }
}
}

View File

@@ -19,51 +19,62 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.s-hover-border {
border: 1px solid transparent;
&:hover {
border-color: rgba($colorSelectableSelectedPrimary, 0.5) !important;
}
}
.s-status-editing {
// Limit to editing mode
$o: 0.5;
$oHover: 0.8;
$bc: $colorSelectableSelectedPrimary;
body.desktop {
.s-hover-border {
// Show a border by default so user can see object bounds and empty objects
border-color: rgba($bc, $o) !important;
border-style: dotted !important;
&:hover {
border-color: rgba($bc, $oHover) !important;
}
&.t-object-type-layout {
border-style: dashed !important;
border-color: rgba($colorBodyFg, 0.3) !important;
}
}
.s-selected {
&.s-moveable {
&:not(.s-drilled-in) {
cursor: move;
.s-status-editing {
// Limit to editing mode
$o: 0.5;
$oHover: 0.8;
$bc: $colorSelectableSelectedPrimary;
.s-hover-border {
// Show a border by default so user can see object bounds and empty objects
border-color: rgba($bc, $o) !important;
border-style: dotted !important;
&:hover {
border-color: rgba($bc, $oHover) !important;
}
&.t-object-type-layout {
border-style: dashed !important;
}
}
.s-selected {
> .s-hover-border,
&.s-hover-border {
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
@include boxShdwLarge();
// Show edit-corners if you got 'em
.edit-corner {
display: block;
&:hover {
background-color: rgba($colorKey, 1);
}
}
}
&.s-moveable {
&:not(.s-drilled-in) {
cursor: move;
}
}
}
}
}
.s-selected > .s-hover-border,
.s-selected.s-hover-border {
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
border-color: $colorSelectableSelectedPrimary !important;
@include boxShdwLarge();
// Show edit-corners if you got 'em
.edit-corner {
display: block;
&:hover {
background-color: rgba($colorKey, 1);
}
.s-selected > .s-hover-border,
.s-selected.s-hover-border {
border-color: $colorSelectableSelectedPrimary !important;
//z-index: 1; // Bring selected item from beneath others. BUT, this breaks editing in Fixed Position.
}
}

View File

@@ -23,17 +23,6 @@
ng-controller="SelectorController as selector">
<div class='col col-15'>
<div class='line field-hints'>Available</div>
<!--div id='_form_filter' class='line filter'>
<input type='text' class='control filter' name='filter-available' />
<a class='icon ui-symbol t-available-trigger'
href=''
title="Filter is case sensitive">M</a>
</div>
<div class="line">
Showing {{shown}} of {{count}} available options.
</div -->
<div class='line treeview' name='available'>
<mct-representation key="'tree'"
mct-object="selector.root()"
@@ -55,18 +44,6 @@
</div>
<div class='col col-15'>
<div class='line field-hints'>Selected</div>
<!-- div id='_form_filter' class='line filter'>
<input type='text' class='control filter' name='filter-selected' />
<a class='icon ui-symbol t-selected-trigger'
href=''
title="Filter is case sensitive">
M
</a>
</div>
<div class="line">
Showing {{shown}} of {{count}} available options.
</div -->
<div class='line treeview l-tree-item-flat-list' name='selected'>
<ul class="tree">
<li ng-repeat="selectedObject in selector.selected()">

View File

@@ -29,14 +29,14 @@
key="'inspector-region'"
mct-object="domainObject"
ng-model="ngModel"
class="flex-elem grows vscroll l-flex-col">
class="flex-elem grows vscroll scroll-pad l-flex-col inspector-properties">
</mct-representation>
</div>
</div>
<mct-splitter class="splitter-inspect-panel mobile-hide"></mct-splitter>
<div class="split-pane-component pane bottom">
<div class="abs holder holder-elements l-flex-col">
<em class="flex-elem t-inspector-part-header">Elements</em>
<h2>Elements</h2>
<mct-representation
key="'edit-elements'"
mct-object="domainObject"

View File

@@ -0,0 +1,45 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}" mct-preview>
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header-frame'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
</div>
</div>
<div class="abs object-holder">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -0,0 +1,55 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
var PREVIEW_TEMPLATE = '<mct-representation key="\'mct-preview\'"' +
'class="t-rep-frame holder"' +
'mct-object="domainObject">' +
'</mct-representation>';
function MCTPreviewAction($compile, $rootScope, context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.$rootScope = $rootScope;
this.$compile = $compile;
}
MCTPreviewAction.prototype.perform = function () {
var newScope = this.$rootScope.$new();
newScope.domainObject = this.domainObject;
this.$compile(PREVIEW_TEMPLATE)(newScope);
};
MCTPreviewAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject,
status = domainObject.getCapability('status');
return !(status && status.get('editing'));
};
return MCTPreviewAction;
}
);

View File

@@ -0,0 +1,64 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['zepto', '../services/Overlay'], function ($, Overlay) {
function MCTPreview($document) {
function link($scope, $element) {
var actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
var notebookButton = notebookAction ?
[
{
class: 'icon-notebook new-notebook-entry',
title: 'New Notebook Entry',
clickHandler: function (event) {
event.stopPropagation();
notebookAction.perform();
}
}
] : [];
var overlayService = new Overlay({
$document: $document,
$element: $element[0],
$scope: $scope,
browseBarButtons: notebookButton
});
overlayService.toggleOverlay();
$scope.$on('$destroy', function () {
$element.remove();
});
}
return {
restrict: 'A',
link: link
};
}
return MCTPreview;
});

View File

@@ -28,6 +28,16 @@ define(
* The mct-selectable directive allows selection functionality
* (click) to be attached to specific elements.
*
* Example of how to use the directive:
*
* mct-selectable="{
* // item is an optional domain object.
* item: domainObject,
* // Can define other arbitrary properties.
* elementProxy: element,
* controller: fixedController
* }"
*
* @memberof platform/commonUI/general
* @constructor
*/

View File

@@ -0,0 +1,185 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining OverlayService. Created by deeptailor on 03/29/2018
*/
define(['zepto'], function ($) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
/*
* An Overlay Service when instantiated creates an overlay dialog.
* @param {Object} options The options object required to instantiate the overlay service
* options = {
* $document: document object,
* $scope: angular $scope object,
* element: node to be injected into overlay as a view,
* overlayWillMount: callback executed before overlay is injected,
* overlayWillUnmount: callback executed before overlay is removed,
* overlayDidMount: callback executed after overlay is injected,
* overlayDidUnmount: callback executed after overlay is removed
* browseBarButtons: an array of desired buttons to be added to the browse bar of the overlay.
* the array should consist of button objects containing:
* a) class - css class to be added to the button div
* b) title - desired button title
* c) clickHandler - callback to be added to the click event listener of the button
* }
* $document, $scope and element are required
*/
function Overlay(options) {
this.element = options.$element;
this.document = options.$document[0];
this.$scope = options.$scope;
this.overlayWillMount = options.overlayWillMount;
this.overlayWillUnmount = options.overlayWillUnmount;
this.overlayDidMount = options.overlayDidMount;
this.overlayDidUnmount = options.overlayDidUnmount;
this.browseBarButtons = options.browseBarButtons || [];
this.buttons = [];
this.openOverlay = this.openOverlay.bind(this);
this.closeOverlay = this.closeOverlay.bind(this);
this.toggleOverlay = this.toggleOverlay.bind(this);
this.removeButtons = this.removeButtons.bind(this);
this.isOverlayOpen = false;
}
Overlay.prototype.openOverlay = function () {
if (this.overlayWillMount && typeof this.overlayWillMount === 'function') {
this.overlayWillMount();
}
this.overlay = this.document.createElement('div');
$(this.overlay).addClass('abs overlay l-large-view');
this.overlay.innerHTML = OVERLAY_TEMPLATE;
this.overlayContainer = this.overlay.querySelector('.t-contents');
this.closeButton = this.overlay.querySelector('a.close');
this.closeButton.addEventListener('click', this.toggleOverlay);
this.doneButton = this.overlay.querySelector('a.t-done');
this.doneButton.addEventListener('click', this.toggleOverlay);
this.blocker = this.overlay.querySelector('.abs.blocker');
this.blocker.addEventListener('click', this.toggleOverlay);
this.document.body.appendChild(this.overlay);
this.overlayContainer.appendChild(this.element);
this.browseBar = this.overlay.querySelector('.object-browse-bar .right');
if (this.browseBarButtons && Array.isArray(this.browseBarButtons)) {
this.browseBarButtons.forEach(function (buttonObject) {
var button = newButtonTemplate(buttonObject.class, buttonObject.title);
this.browseBar.prepend(button);
button.addEventListener('click', buttonObject.clickHandler);
this.buttons.push(button);
}.bind(this));
}
if (this.overlayDidMount && typeof this.overlayDidMount === 'function') {
this.overlayDidMount();
}
};
Overlay.prototype.closeOverlay = function () {
if (this.overlayWillUnmount && typeof this.overlayWillUnmount === 'function') {
this.overlayWillUnmount();
}
this.overlayContainer.removeChild(this.element);
this.document.body.removeChild(this.overlay);
this.closeButton.removeEventListener('click', this.toggleOverlay);
this.closeButton = undefined;
this.doneButton.removeEventListener('click', this.toggleOverlay);
this.doneButton = undefined;
this.blocker.removeEventListener('click', this.toggleOverlay);
this.blocker = undefined;
this.overlayContainer = undefined;
this.overlay = undefined;
this.removeButtons();
if (this.overlayDidUnmount && typeof this.overlayDidUnmount === 'function') {
this.overlayDidUnmount();
}
};
Overlay.prototype.toggleOverlay = function (event) {
if (event) {
event.stopPropagation();
}
if (!this.isOverlayOpen) {
this.openOverlay();
this.isOverlayOpen = true;
} else {
this.closeOverlay();
this.isOverlayOpen = false;
}
};
Overlay.prototype.removeButtons = function () {
this.buttons.forEach(function (button) {
button.remove();
}.bind(this));
this.buttons = [];
};
function newButtonTemplate(classString, title) {
var NEW_BUTTON_TEMPLATE = '<a class="s-button labeled' + classString + '">' +
'<span class="title-label">' + title + '</span>' +
'</a>';
var button = document.createElement('div');
$(button).addClass('holder flex-elem');
button.innerHTML = NEW_BUTTON_TEMPLATE;
return button;
}
return Overlay;
});

View File

@@ -31,8 +31,8 @@
* @namespace platform/commonUI/notification
*/
define(
[],
function () {
['moment'],
function (moment) {
/**
* A representation of a user action. Options are provided to
@@ -313,6 +313,7 @@ define(
topic = this.topic();
notificationModel.severity = notificationModel.severity || "info";
notificationModel.timestamp = moment.utc().format('YYYY-MM-DD hh:mm:ss.ms');
notification = {
model: notificationModel,

View File

@@ -72,8 +72,8 @@ $colorObjHdrTxt: $colorBodyFg;
$colorObjHdrIc: lighten($colorObjHdrTxt, 20%);
$colorTick: rgba(white, 0.2);
$colorSelectableSelectedPrimary: $colorKey;
$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
$colorSelectableHov: $colorSelectableSelectedSecondary;
//$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
$colorSelectableHov: rgba($colorBodyFg, 0.3);
// Menu colors
$colorMenuBg: pullForward($colorBodyBg, 23%);
@@ -226,8 +226,6 @@ $colorSplitterHover: pullForward($colorSplitterBg, $hoverRatioPercent);
$colorSplitterActive: $colorKey;
// Minitabs
$uePaneMiniTabH: 24px;
$uePaneMiniTabW: 8px;
$colorMiniTabBg: $colorSplitterBg;
$colorMiniTabFg: pushBack($colorMiniTabBg, 10%);
$colorMiniTabBgHov: $colorSplitterHover;

View File

@@ -72,8 +72,8 @@ $colorObjHdrTxt: $colorBodyFg;
$colorObjHdrIc: lighten($colorObjHdrTxt, 30%);
$colorTick: rgba(black, 0.2);
$colorSelectableSelectedPrimary: $colorKey;
$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
$colorSelectableHov: $colorSelectableSelectedSecondary;
//$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
$colorSelectableHov: rgba($colorBodyFg, 0.4);
// Menu colors
$colorMenuBg: pushBack($colorBodyBg, 10%);
@@ -226,8 +226,6 @@ $colorSplitterHover: pullForward($colorSplitterBg, $hoverRatioPercent * 2);
$colorSplitterActive: $colorKey;
// Minitabs
$uePaneMiniTabH: 24px;
$uePaneMiniTabW: 8px;
$colorMiniTabBg: $colorSplitterBg;
$colorMiniTabFg: pullForward($colorMiniTabBg, 30%);
$colorMiniTabBgHov: $colorSplitterHover;

View File

@@ -111,9 +111,11 @@ define(
CopyAction.prototype.perform = function () {
var self = this;
function success() {
function success(domainObject) {
var domainObjectName = domainObject.model.name;
self.notification.dismiss();
self.notificationService.info("Copying complete.");
self.notificationService.info(domainObjectName + " copied successfully.");
}
function error(errorDetails) {
@@ -160,4 +162,3 @@ define(
return CopyAction;
}
);

View File

@@ -47,6 +47,7 @@ define(
mockDialog,
mockLog,
abstractComposePromise,
domainObject = {model: {name: "mockObject"}},
progress = {phase: "copying", totalObjects: 10, processed: 1};
beforeEach(function () {
@@ -108,7 +109,7 @@ define(
abstractComposePromise.then.andCallFake(function (success, error, notify) {
notify(progress);
success();
success(domainObject);
});
locationServicePromise.then.andCallFake(function (callback) {
@@ -199,6 +200,10 @@ define(
expect(notificationService.info).toHaveBeenCalled();
});
it("notifies the user with name of object copied", function () {
expect(notificationService.info)
.toHaveBeenCalledWith("mockObject copied successfully.");
});
});
});

View File

@@ -28,6 +28,7 @@ define([
"./src/ui/TimeOfInterestController",
"./src/ui/ConductorAxisDirective",
"./src/ui/NumberFormat",
"./src/ui/StringFormat",
"text!./res/templates/time-conductor.html",
"text!./res/templates/mode-selector/mode-selector.html",
"text!./res/templates/mode-selector/mode-menu.html",
@@ -41,6 +42,7 @@ define([
TimeOfInterestController,
ConductorAxisDirective,
NumberFormat,
StringFormat,
timeConductorTemplate,
modeSelectorTemplate,
modeMenuTemplate,
@@ -145,6 +147,10 @@ define([
{
"key": "number",
"implementation": NumberFormat
},
{
"key": "string",
"implementation": StringFormat
}
]
}

View File

@@ -1,5 +1,5 @@
$ueTimeConductorH: (25px, 16px, 20px); // Heights for Ticks, Data Visualization, Controls elements
$ueTimeConductorRtH: (25px, 3px, 20px); // Heights for elements in Real-time mode
$ueTimeConductorH: (20px, 3px, 20px); // Heights for Ticks, Data Visualization, Controls elements
$ueTimeConductorRtH: (20px, 3px, 20px); // Heights for elements in Real-time mode
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
$timeCondInputDeltaDefW: 65px; // Default width for delta value inputs, typically 00:00:00
$timeCondTOIIconD: 12px; // height and width of icon used for TOI indicator

View File

@@ -192,15 +192,19 @@
width: 100%;
height: 100%;
> g {
// Tick holder
font-size: 0.9em;
transform: translateY(23px);
}
path {
path,
line {
// Line beneath ticks
display: none;
}
line {
// Tick marks
stroke: $c;
//stroke: $c;
//transform: translateY(-10px);
}
text {
// Tick labels
@@ -256,7 +260,7 @@
.l-time-conductor-controls {
align-items: center;
margin-top: $interiorMargin;
margin-top: $interiorMarginLg;
.l-time-conductor-zoom-w {
justify-content: flex-end;
.time-conductor-zoom {
@@ -334,27 +338,25 @@
content: $i;
}
.l-axis-holder {
$angle: 90deg;
$c0: rgba($colorBodyFg, 0.1);
$c1: rgba($colorBodyFg, 0.1);
$c2: transparent;
$grabTicksH: 3px;
$grabTicksXSpace: 4px;
$grabTicksYOffset: 0;
$grabTicksYOffset: 2px;
$grabTicksXSpace: 3px;
transition: background 150ms ease-in-out;
@include cursorGrab();
svg {
$c1: rgba($colorBodyFg, 0.2);
$angle: 90deg;
@include background-image(linear-gradient($angle,
$c1 1px, $c2 1px,
$c2 100%
));
background-position: center $grabTicksYOffset;
background-repeat: repeat-x;
background-size: $grabTicksXSpace $grabTicksH;
background-size: $grabTicksXSpace $r1H - ($grabTicksYOffset * 2);
}
&:hover {
@include background-image(linear-gradient(
$c0 70%, $c2 100%
));
background: $c0;
}
}
}

View File

@@ -77,7 +77,7 @@
</span>
</span>
<input type="submit" class="off">
<input type="submit" class="invisible">
</form>
<conductor-axis class="mobile-hide" view-service="tcController.conductorViewService"></conductor-axis>
</div>

View File

@@ -76,9 +76,8 @@ define(
this.xAxis = d3Axis.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) + ")");
// draw x axis with labels. CSS is used to position them.
this.axisElement = vis.append("g");
if (this.timeAPI.timeSystem() !== undefined) {
this.changeTimeSystem(this.timeAPI.timeSystem());

View File

@@ -0,0 +1,53 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
/**
* Formatter for basic strings.
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function StringFormat() {
this.key = 'string';
}
StringFormat.prototype.format = function (string) {
if (typeof string === 'string') {
return string;
} else {
return '' + string;
}
};
StringFormat.prototype.parse = function (string) {
return string;
};
StringFormat.prototype.validate = function (string) {
return typeof string === 'string';
};
return StringFormat;
});

View File

@@ -39,240 +39,309 @@ define([
"cssClass": "icon-box-with-dashed-lines",
"type": "telemetry.fixed",
"template": fixedTemplate,
"uses": [
"composition"
],
"editable": true,
"toolbar": {
"sections": [
"uses": [],
"editable": true
}
],
"toolbars": [
{
name: "Fixed Position Toolbar",
key: "fixed.position",
description: "Toolbar for the selected element inside a fixed position display.",
forSelection: function (selection) {
if (!selection) {
return;
}
return (
selection[0] && selection[0].context.elementProxy &&
selection[1] && selection[1].context.item.type === 'telemetry.fixed' ||
selection[0] && selection[0].context.item.type === 'telemetry.fixed'
);
},
toolbar: function (selection) {
var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"];
var boxProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill"];
var textProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "text"];
var lineProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "x2", "y2"];
var telemetryProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "titled"];
var fixedPageProperties = ["add"];
var properties = [],
fixedItem = selection[0] && selection[0].context.item,
elementProxy = selection[0] && selection[0].context.elementProxy,
domainObject = selection[1] && selection[1].context.item,
path;
if (elementProxy) {
var type = elementProxy.element.type;
path = "configuration['fixed-display'].elements[" + elementProxy.index + "]";
properties =
type === 'fixed.image' ? imageProperties :
type === 'fixed.text' ? textProperties :
type === 'fixed.box' ? boxProperties :
type === 'fixed.line' ? lineProperties :
type === 'fixed.telemetry' ? telemetryProperties : [];
} else if (fixedItem) {
properties = domainObject && domainObject.type === 'layout' ? [] : fixedPageProperties;
}
return [
{
"items": [
control: "menu-button",
domainObject: domainObject || selection[0].context.item,
method: function (value) {
selection[0].context.fixedController.add(value);
},
key: "add",
cssClass: "icon-plus",
text: "Add",
options: [
{
"method": "add",
"cssClass": "icon-plus",
"control": "menu-button",
"text": "Add",
"options": [
{
"name": "Box",
"cssClass": "icon-box",
"key": "fixed.box"
},
{
"name": "Line",
"cssClass": "icon-line-horz",
"key": "fixed.line"
},
{
"name": "Text",
"cssClass": "icon-T",
"key": "fixed.text"
},
{
"name": "Image",
"cssClass": "icon-image",
"key": "fixed.image"
}
]
}
]
},
{
"items": [
{
"method": "order",
"cssClass": "icon-layers",
"control": "menu-button",
"title": "Layering",
"description": "Move the selected object above or below other objects",
"options": [
{
"name": "Move to Top",
"cssClass": "icon-arrow-double-up",
"key": "top"
},
{
"name": "Move Up",
"cssClass": "icon-arrow-up",
"key": "up"
},
{
"name": "Move Down",
"cssClass": "icon-arrow-down",
"key": "down"
},
{
"name": "Move to Bottom",
"cssClass": "icon-arrow-double-down",
"key": "bottom"
}
]
"name": "Box",
"cssClass": "icon-box",
"key": "fixed.box"
},
{
"property": "fill",
"cssClass": "icon-paint-bucket",
"title": "Fill color",
"description": "Set fill color",
"control": "color"
},
{
"property": "stroke",
"name": "Line",
"cssClass": "icon-line-horz",
"title": "Border color",
"description": "Set border color",
"control": "color"
"key": "fixed.line"
},
{
"property": "url",
"cssClass": "icon-image",
"control": "dialog-button",
"title": "Image Properties",
"description": "Edit image properties",
"dialog": {
"control": "textfield",
"name": "Image URL",
"cssClass": "l-input-lg",
"required": true
}
}
]
},
{
"items": [
{
"property": "color",
"name": "Text",
"cssClass": "icon-T",
"title": "Text color",
"description": "Set text color",
"mandatory": true,
"control": "color"
"key": "fixed.text"
},
{
"property": "size",
"title": "Text size",
"description": "Set text size",
"control": "select",
"options": [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
return { "name": size + " px", "value": size + "px" };
})
"name": "Image",
"cssClass": "icon-image",
"key": "fixed.image"
}
]
},
{
"items": [
control: "menu-button",
domainObject: domainObject,
method: function (value) {
selection[0].context.fixedController.order(
selection[0].context.elementProxy,
value
);
},
key: "order",
cssClass: "icon-layers",
title: "Layering",
description: "Move the selected object above or below other objects",
options: [
{
"property": "editX",
"text": "X",
"name": "X",
"cssClass": "l-input-sm",
"control": "numberfield",
"min": "0"
"name": "Move to Top",
"cssClass": "icon-arrow-double-up",
"key": "top"
},
{
"property": "editY",
"text": "Y",
"name": "Y",
"cssClass": "l-input-sm",
"control": "numberfield",
"min": "0"
"name": "Move Up",
"cssClass": "icon-arrow-up",
"key": "up"
},
{
"property": "editX1",
"text": "X1",
"name": "X1",
"cssClass": "l-input-sm",
"control" : "numberfield",
"min": "0"
"name": "Move Down",
"cssClass": "icon-arrow-down",
"key": "down"
},
{
"property": "editY1",
"text": "Y1",
"name": "Y1",
"cssClass": "l-input-sm",
"control" : "numberfield",
"min": "0"
},
{
"property": "editX2",
"text": "X2",
"name": "X2",
"cssClass": "l-input-sm",
"control" : "numberfield",
"min": "0"
},
{
"property": "editY2",
"text": "Y2",
"name": "Y2",
"cssClass": "l-input-sm",
"control" : "numberfield",
"min": "0"
},
{
"property": "editHeight",
"text": "H",
"name": "H",
"cssClass": "l-input-sm",
"control": "numberfield",
"description": "Resize object height",
"min": "1"
},
{
"property": "editWidth",
"text": "W",
"name": "W",
"cssClass": "l-input-sm",
"control": "numberfield",
"description": "Resize object width",
"min": "1"
},
{
"property": "useGrid",
"name": "Snap to Grid",
"control": "checkbox"
"name": "Move to Bottom",
"cssClass": "icon-arrow-double-down",
"key": "bottom"
}
]
},
{
"items": [
{
"property": "text",
"cssClass": "icon-gear",
"control": "dialog-button",
"title": "Text Properties",
"description": "Edit text properties",
"dialog": {
"control": "textfield",
"name": "Text",
"required": true
}
},
{
"method": "showTitle",
"cssClass": "icon-two-parts-both",
"control": "button",
"title": "Show title",
"description": "Show telemetry element title"
},
{
"method": "hideTitle",
"cssClass": "icon-two-parts-one-only",
"control": "button",
"title": "Hide title",
"description": "Hide telemetry element title"
}
]
control: "color",
domainObject: domainObject,
property: path + ".fill",
cssClass: "icon-paint-bucket",
title: "Fill color",
description: "Set fill color",
key: 'fill'
},
{
"items": [
{
"method": "remove",
"control": "button",
"cssClass": "icon-trash"
}
]
control: "color",
domainObject: domainObject,
property: path + ".stroke",
cssClass: "icon-line-horz",
title: "Border color",
description: "Set border color",
key: 'stroke'
},
{
control: "dialog-button",
domainObject: domainObject,
property: path + ".url",
cssClass: "icon-image",
title: "Image Properties",
description: "Edit image properties",
key: 'url',
dialog: {
control: "textfield",
name: "Image URL",
cssClass: "l-input-lg",
required: true
}
},
{
control: "color",
domainObject: domainObject,
property: path + ".color",
cssClass: "icon-T",
title: "Text color",
mandatory: true,
description: "Set text color",
key: 'color'
},
{
control: "select",
domainObject: domainObject,
property: path + ".size",
title: "Text size",
description: "Set text size",
"options": [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
return { "name": size + " px", "value": size + "px" };
}),
key: 'size'
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".x",
text: "X",
name: "X",
key: "x",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".y",
text: "Y",
name: "Y",
key: "y",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".x",
text: "X1",
name: "X1",
key: "x1",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".y",
text: "Y1",
name: "Y1",
key: "y1",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".x2",
text: "X2",
name: "X2",
key: "x2",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".y2",
text: "Y2",
name: "Y2",
key: "y2",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".height",
text: "H",
name: "H",
key: "height",
cssClass: "l-input-sm",
description: "Resize object height",
min: "1"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".width",
text: "W",
name: "W",
key: "width",
cssClass: "l-input-sm",
description: "Resize object width",
min: "1"
},
{
control: "checkbox",
domainObject: domainObject,
property: path + ".useGrid",
name: "Snap to Grid",
key: "useGrid"
},
{
control: "dialog-button",
domainObject: domainObject,
property: path + ".text",
cssClass: "icon-gear",
title: "Text Properties",
description: "Edit text properties",
key: "text",
dialog: {
control: "textfield",
name: "Text",
required: true
}
},
{
control: "checkbox",
domainObject: domainObject,
property: path + ".titled",
name: "Show Title",
key: "titled"
},
{
control: "button",
domainObject: domainObject,
method: function () {
selection[0].context.fixedController.remove(
selection[0].context.elementProxy
);
},
key: "remove",
cssClass: "icon-trash"
}
]
].filter(function (item) {
var filtered;
properties.forEach(function (property) {
if (item.property && item.key === property ||
item.method && item.key === property) {
filtered = item;
}
});
return filtered;
});
}
}
],

View File

@@ -1,10 +1,9 @@
<div class="t-imagery" ng-controller="ImageryController as imagery">
<mct-split-pane class='abs' anchor="bottom" alias="imagery">
<div class="split-pane-component l-image-main-wrapper l-flex-col"
<div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col"
ng-mouseenter="showLocalControls = true;"
ng-mouseleave="showLocalControls = false;">
<div class="l-local-controls s-local-controls s-wrapper-transluc l-flex-row"
ng-class="{ 'hide-nice': !showLocalControls }">
<div class="h-local-controls h-local-controls-overlay-content h-local-controls-trans s-local-controls local-controls-hidden l-flex-row">
<span class="holder flex-elem grows">
<input class="icon-brightness" type="range"
min="0"
@@ -38,18 +37,12 @@
ng-click="showThumbsBubble = (showThumbsBubble) ? false:true"></a>
<span class="l-time">{{imagery.getTime()}}</span>
</div>
<div class="l-controls-w flex-elem">
<div class="h-local-controls flex-elem">
<a class="s-button pause-play"
ng-click="imagery.paused(!imagery.paused())"
ng-class="{ paused: imagery.paused() }"></a>
<a href="{{imagery.getImageUrl()}}"
ng-if="imagery.getImageUrl()"
target="_blank"
title="Open image in new tab."
class="s-button icon-new-window">
</a>
<a href=""
class="s-button l-mag s-mag ui-symbol vsm icon-arrows-out"
class="s-button l-mag s-mag vsm icon-reset"
ng-click="clipped = false"
ng-show="clipped === true"
title="Not all of image is visible; click to reset."></a>

View File

@@ -161,13 +161,30 @@ define(
* @returns {boolean} falsy when a duplicate datum is given
*/
ImageryController.prototype.updateHistory = function (datum) {
if (this.$scope.imageHistory.length === 0 ||
!_.isEqual(this.$scope.imageHistory.slice(-1)[0], datum)) {
if (!this.datumMatchesMostRecent(datum)) {
var index = _.sortedIndex(this.$scope.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
this.$scope.imageHistory.splice(index, 0, datum);
return true;
} else {
return false;
}
};
/**
* Checks to see if the given datum is the same as the most recent in history.
* @private
* @param {object} [datum] target telemetry datum
* @returns {boolean} true if datum is most recent in history, false otherwise
*/
ImageryController.prototype.datumMatchesMostRecent = function (datum) {
if (this.$scope.imageHistory.length !== 0) {
var datumTime = this.timeFormat.format(datum);
var datumURL = this.imageFormat.format(datum);
var lastHistoryTime = this.timeFormat.format(this.$scope.imageHistory.slice(-1)[0]);
var lastHistoryURL = this.imageFormat.format(this.$scope.imageHistory.slice(-1)[0]);
return datumTime === lastHistoryTime && datumURL === lastHistoryURL;
}
return false;
};

View File

@@ -230,10 +230,14 @@ define(
});
it ("doesnt append duplicate datum", function () {
var mockDatum = {url: 'image/url', utc: 1434600000000};
var mockDatum = {value: 'image/url', timestamp: 1434700000000};
var mockDatum2 = {value: 'image/url', timestamp: 1434700000000};
var mockDatum3 = {value: 'image/url', url: 'someval', timestamp: 1434700000000};
expect(controller.updateHistory(mockDatum)).toBe(true);
expect(controller.updateHistory(mockDatum)).toBe(false);
expect(controller.updateHistory(mockDatum)).toBe(false);
expect(controller.updateHistory(mockDatum2)).toBe(false);
expect(controller.updateHistory(mockDatum3)).toBe(false);
});
describe("when user clicks on imagery thumbnail", function () {

View File

@@ -62,29 +62,7 @@ define([
"type": "layout",
"template": layoutTemplate,
"editable": true,
"uses": [],
"toolbar": {
"sections": [
{
"items": [
{
"method": "showFrame",
"cssClass": "icon-frame-show",
"control": "button",
"title": "Show frame",
"description": "Show frame"
},
{
"method": "hideFrame",
"cssClass": "icon-frame-hide",
"control": "button",
"title": "Hide frame",
"description": "Hide frame"
}
]
}
]
}
"uses": []
},
{
"key": "fixed",
@@ -305,6 +283,27 @@ define([
"implementation": LayoutCompositionPolicy
}
],
"toolbars": [
{
name: "Display Layout Toolbar",
key: "layout",
description: "A toolbar for objects inside a display layout.",
forSelection: function (selection) {
// Apply the layout toolbar if the selected object is inside a layout.
return (selection && selection[1] && selection[1].context.item.type === 'layout');
},
toolbar: function (selection) {
return [
{
control: "checkbox",
name: "Show frame",
domainObject: selection[1].context.item,
property: "configuration.layout.panels[" + selection[0].context.oldItem.id + "].hasFrame"
}
];
}
}
],
"types": [
{
"key": "layout",
@@ -314,7 +313,14 @@ define([
"priority": 900,
"features": "creation",
"model": {
"composition": []
"composition": [],
configuration: {
layout: {
panels: {
}
}
}
},
"properties": [
{

View File

@@ -41,7 +41,7 @@
<mct-include key="element.template"
parameters="{ gridSize: controller.getGridSize() }"
ng-model="element">
</mct-include>
</mct-include>
</div>
<!-- Selection highlight, handles -->
<span class="s-selected s-moveable" ng-if="controller.isElementSelected()">

View File

@@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="frame frame-template t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}">
<div class="frame frame-template t-frame-inner abs has-local-controls t-object-type-{{ domainObject.getModel().type }}">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
@@ -28,7 +28,7 @@
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed h-local-controls local-controls-hidden">
<mct-representation
key="'switcher'"
ng-model="representation"

View File

@@ -41,7 +41,7 @@
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-drilled-in': controller.isDrilledIn(childObject) }"
ng-repeat="childObject in composition"
ng-init="controller.selectIfNew(childObject.getId() + '-' + $id, childObject)"
mct-selectable="controller.getContext(childObject, true)"
mct-selectable="controller.getContext(childObject)"
ng-dblclick="controller.drill($event, childObject)"
ng-style="controller.getFrameStyle(childObject.getId())">

View File

@@ -38,6 +38,24 @@ define(
var DEFAULT_DIMENSIONS = [2, 1];
// Convert from element x/y/width/height to an
// appropriate ng-style argument, to position elements.
function convertPosition(elementProxy) {
if (elementProxy.getStyle) {
return elementProxy.getStyle();
}
var gridSize = elementProxy.getGridSize();
// Multiply position/dimensions by grid size
return {
left: (gridSize[0] * elementProxy.element.x) + 'px',
top: (gridSize[1] * elementProxy.element.y) + 'px',
width: (gridSize[0] * elementProxy.element.width) + 'px',
height: (gridSize[1] * elementProxy.element.height) + 'px'
};
}
/**
* The FixedController is responsible for supporting the
* Fixed Position view. It arranges frames according to saved
@@ -51,14 +69,14 @@ define(
this.names = {}; // Cache names by ID
this.values = {}; // Cache values by ID
this.elementProxiesById = {};
this.telemetryObjects = [];
this.subscriptions = [];
this.telemetryObjects = {};
this.subscriptions = {};
this.openmct = openmct;
this.$element = $element;
this.$scope = $scope;
this.gridSize = $scope.domainObject && $scope.domainObject.getModel().layoutGrid;
this.dialogService = dialogService;
this.$q = $q;
this.newDomainObject = $scope.domainObject.useCapability('adapter');
this.fixedViewSelectable = false;
var self = this;
@@ -67,59 +85,13 @@ define(
'fetchHistoricalData',
'getTelemetry',
'setDisplayedValue',
'subscribeToObjects',
'subscribeToObject',
'unsubscribe',
'updateView'
].forEach(function (name) {
self[name] = self[name].bind(self);
});
// Convert from element x/y/width/height to an
// appropriate ng-style argument, to position elements.
function convertPosition(elementProxy) {
var gridSize = elementProxy.getGridSize();
// Multiply position/dimensions by grid size
return {
left: (gridSize[0] * elementProxy.x()) + 'px',
top: (gridSize[1] * elementProxy.y()) + 'px',
width: (gridSize[0] * elementProxy.width()) + 'px',
height: (gridSize[1] * elementProxy.height()) + 'px'
};
}
// Update the style for a selected element
function updateSelectionStyle() {
if (self.selectedElementProxy) {
self.selectedElementProxy.style = convertPosition(self.selectedElementProxy);
}
}
// Generate a specific drag handle
function generateDragHandle(elementHandle) {
return new FixedDragHandle(
elementHandle,
self.gridSize,
updateSelectionStyle,
$scope.commit
);
}
// Generate drag handles for an element
function generateDragHandles(element) {
return element.handles().map(generateDragHandle);
}
// Update element positions when grid size changes
function updateElementPositions(layoutGrid) {
// Update grid size from model
self.gridSize = layoutGrid;
self.elementProxies.forEach(function (elementProxy) {
elementProxy.setGridSize(self.gridSize);
elementProxy.style = convertPosition(elementProxy);
});
}
// Decorate an element for display
function makeProxyElement(element, index, elements) {
var ElementProxy = ElementProxies[element.type],
@@ -137,14 +109,14 @@ define(
// Decorate elements in the current configuration
function refreshElements() {
var elements = (($scope.configuration || {}).elements || []);
var elements = (((self.newDomainObject.configuration || {})['fixed-display'] || {}).elements || []);
// Create the new proxies...
self.elementProxies = elements.map(makeProxyElement);
// If selection is not in array, select parent.
// Otherwise, set the element to select after refresh.
if (self.selectedElementProxy) {
// If selection is not in array, select parent.
// Otherwise, set the element to select after refresh.
var index = elements.indexOf(self.selectedElementProxy.element);
if (index === -1) {
self.$element[0].click();
@@ -168,79 +140,53 @@ define(
});
}
function removeObjects(ids) {
var configuration = self.$scope.configuration;
if (configuration &&
configuration.elements) {
configuration.elements = configuration.elements.filter(function (proxy) {
return ids.indexOf(proxy.id) === -1;
});
}
self.getTelemetry($scope.domainObject);
refreshElements();
// Mark change as persistable
if (self.$scope.commit) {
self.$scope.commit("Objects removed.");
}
}
// Handle changes in the object's composition
function updateComposition(composition, previousComposition) {
var removedIds = [];
// Resubscribe - objects in view have changed
if (composition !== previousComposition) {
//remove any elements no longer in the composition
removedIds = _.difference(previousComposition, composition);
if (removedIds.length > 0) {
removeObjects(removedIds);
}
}
}
// Trigger a new query for telemetry data
function updateDisplayBounds(bounds, isTick) {
if (!isTick) {
//Reset values
self.values = {};
refreshElements();
//Fetch new data
self.fetchHistoricalData(self.telemetryObjects);
Object.values(self.telemetryObjects).forEach(function (object) {
self.fetchHistoricalData(object);
});
}
}
// Add an element to this view
function addElement(element) {
// Ensure that configuration field is populated
$scope.configuration = $scope.configuration || {};
// Make sure there is a "elements" field in the
// view configuration.
$scope.configuration.elements =
$scope.configuration.elements || [];
// Store the position of this element.
$scope.configuration.elements.push(element);
var index;
var elements = (((self.newDomainObject.configuration || {})['fixed-display'] || {}).elements || []);
elements.push(element);
self.elementToSelectAfterRefresh = element;
// Refresh displayed elements
refreshElements();
// Mark change as persistable
if ($scope.commit) {
$scope.commit("Dropped an element.");
if (self.selectedElementProxy) {
index = elements.indexOf(self.selectedElementProxy.element);
}
self.mutate("configuration['fixed-display'].elements", elements);
elements = (self.newDomainObject.configuration)['fixed-display'].elements || [];
self.elementToSelectAfterRefresh = elements[elements.length - 1];
if (self.selectedElementProxy) {
// Update the selected element with the new
// value since newDomainOject is mutated.
self.selectedElementProxy.element = elements[index];
}
refreshElements();
}
// Position a panel after a drop event
function handleDrop(e, id, position) {
// Don't handle this event if it has already been handled
// color is set to "" to let the CSS theme determine the default color
if (e.defaultPrevented) {
return;
}
e.preventDefault();
// Store the position of this element.
// color is set to "" to let the CSS theme determine the default color
addElement({
type: "fixed.telemetry",
x: Math.floor(position.x / self.gridSize[0]),
@@ -254,71 +200,229 @@ define(
useGrid: true
});
//Re-initialize objects, and subscribe to new object
self.getTelemetry($scope.domainObject);
}
// Sets the selectable object in response to the selection change event.
function setSelection(selectable) {
var selection = selectable[0];
if (!selection) {
return;
}
if (selection.context.elementProxy) {
self.selectedElementProxy = selection.context.elementProxy;
self.mvHandle = self.generateDragHandle(self.selectedElementProxy);
self.resizeHandles = self.generateDragHandles(self.selectedElementProxy);
} else {
// Make fixed view selectable if it's not already.
if (!self.fixedViewSelectable && selectable.length === 1) {
self.fixedViewSelectable = true;
selection.context.viewProxy = new FixedProxy(addElement, $q, dialogService);
self.openmct.selection.select(selection);
}
self.resizeHandles = [];
self.mvHandle = undefined;
self.selectedElementProxy = undefined;
}
// Subscribe to the new object to get telemetry
self.openmct.objects.get(id).then(function (object) {
self.getTelemetry(object);
});
}
this.elementProxies = [];
this.generateDragHandle = generateDragHandle;
this.generateDragHandles = generateDragHandles;
this.updateSelectionStyle = updateSelectionStyle;
this.addElement = addElement;
this.refreshElements = refreshElements;
this.fixedProxy = new FixedProxy(this.addElement, this.$q, this.dialogService);
// Detect changes to grid size
$scope.$watch("model.layoutGrid", updateElementPositions);
this.composition = this.openmct.composition.get(this.newDomainObject);
this.composition.on('add', this.onCompositionAdd, this);
this.composition.on('remove', this.onCompositionRemove, this);
this.composition.load();
// Position panes where they are dropped
$scope.$on("mctDrop", handleDrop);
// Position panes when the model field changes
$scope.$watch("model.composition", updateComposition);
// Refresh list of elements whenever model changes
$scope.$watch("model.modified", refreshElements);
// Subscribe to telemetry when an object is available
$scope.$watch("domainObject", this.getTelemetry);
// Free up subscription on destroy
$scope.$on("$destroy", function () {
self.unsubscribe();
self.openmct.time.off("bounds", updateDisplayBounds);
self.openmct.selection.off("change", setSelection);
});
$scope.$on("$destroy", this.destroy.bind(this));
// Respond to external bounds changes
this.openmct.time.on("bounds", updateDisplayBounds);
this.openmct.selection.on('change', setSelection);
this.$element.on('click', this.bypassSelection.bind(this));
setSelection(this.openmct.selection.get());
this.openmct.selection.on('change', this.setSelection.bind(this));
this.$element.on('click', this.bypassSelection.bind(this));
this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
this.newDomainObject = JSON.parse(JSON.stringify(obj));
this.updateElementPositions(this.newDomainObject.layoutGrid);
}.bind(this));
this.updateElementPositions(this.newDomainObject.layoutGrid);
refreshElements();
}
FixedController.prototype.updateElementPositions = function (layoutGrid) {
this.gridSize = layoutGrid;
this.elementProxies.forEach(function (elementProxy) {
elementProxy.setGridSize(this.gridSize);
elementProxy.style = convertPosition(elementProxy);
}.bind(this));
};
FixedController.prototype.onCompositionAdd = function (object) {
this.getTelemetry(object);
};
FixedController.prototype.onCompositionRemove = function (identifier) {
// Defer mutation of newDomainObject to prevent mutating an
// outdated version since this is triggered by a composition change.
setTimeout(function () {
var id = objectUtils.makeKeyString(identifier);
var elements = this.newDomainObject.configuration['fixed-display'].elements || [];
var newElements = elements.filter(function (proxy) {
return proxy.id !== id;
});
this.mutate("configuration['fixed-display'].elements", newElements);
if (this.subscriptions[id]) {
this.subscriptions[id]();
delete this.subscriptions[id];
}
delete this.telemetryObjects[id];
this.refreshElements();
}.bind(this));
};
/**
* Removes an element from the view.
*
* @param {Object} elementProxy the element proxy to remove.
*/
FixedController.prototype.remove = function (elementProxy) {
var element = elementProxy.element;
var elements = this.newDomainObject.configuration['fixed-display'].elements || [];
elements.splice(elements.indexOf(element), 1);
if (element.type === 'fixed.telemetry') {
this.newDomainObject.composition = this.newDomainObject.composition.filter(function (identifier) {
return objectUtils.makeKeyString(identifier) !== element.id;
});
}
this.mutate("configuration['fixed-display'].elements", elements);
this.refreshElements();
};
/**
* Adds a new element to the view.
*
* @param {string} type the type of element to add. Supported types are:
* `fixed.image`
* `fixed.box`
* `fixed.text`
* `fixed.line`
*/
FixedController.prototype.add = function (type) {
this.fixedProxy.add(type);
};
/**
* Change the display order of the element proxy.
*/
FixedController.prototype.order = function (elementProxy, position) {
var elements = elementProxy.order(position);
// Find the selected element index in the updated array.
var selectedElemenetIndex = elements.indexOf(this.selectedElementProxy.element);
this.mutate("configuration['fixed-display'].elements", elements);
elements = (this.newDomainObject.configuration)['fixed-display'].elements || [];
// Update the selected element with the new
// value since newDomainOject is mutated.
this.selectedElementProxy.element = elements[selectedElemenetIndex];
this.refreshElements();
};
FixedController.prototype.generateDragHandle = function (elementProxy, elementHandle) {
var index = this.elementProxies.indexOf(elementProxy);
if (elementHandle) {
elementHandle.element = elementProxy.element;
elementProxy = elementHandle;
}
return new FixedDragHandle(
elementProxy,
"configuration['fixed-display'].elements[" + index + "]",
this
);
};
FixedController.prototype.generateDragHandles = function (elementProxy) {
return elementProxy.handles().map(function (handle) {
return this.generateDragHandle(elementProxy, handle);
}, this);
};
FixedController.prototype.updateSelectionStyle = function () {
this.selectedElementProxy.style = convertPosition(this.selectedElementProxy);
};
FixedController.prototype.setSelection = function (selectable) {
var selection = selectable[0];
if (this.selectionListeners) {
this.selectionListeners.forEach(function (l) {
l();
});
}
this.selectionListeners = [];
if (!selection) {
return;
}
if (selection.context.elementProxy) {
this.selectedElementProxy = selection.context.elementProxy;
this.attachSelectionListeners();
this.mvHandle = this.generateDragHandle(this.selectedElementProxy);
this.resizeHandles = this.generateDragHandles(this.selectedElementProxy);
} else {
// Make fixed view selectable if it's not already.
if (!this.fixedViewSelectable && selectable.length === 1) {
this.fixedViewSelectable = true;
selection.context.fixedController = this;
this.openmct.selection.select(selection);
}
this.resizeHandles = [];
this.mvHandle = undefined;
this.selectedElementProxy = undefined;
}
};
FixedController.prototype.attachSelectionListeners = function () {
var index = this.elementProxies.indexOf(this.selectedElementProxy);
var path = "configuration['fixed-display'].elements[" + index + "]";
this.selectionListeners.push(this.openmct.objects.observe(this.newDomainObject, path + ".useGrid", function (newValue) {
if (this.selectedElementProxy.useGrid() !== newValue) {
this.selectedElementProxy.useGrid(newValue);
this.updateSelectionStyle();
this.openmct.objects.mutate(this.newDomainObject, path, this.selectedElementProxy.element);
}
}.bind(this)));
[
"width",
"height",
"stroke",
"fill",
"x",
"y",
"x1",
"y1",
"x2",
"y2",
"color",
"size",
"text",
"titled"
].forEach(function (property) {
this.selectionListeners.push(this.openmct.objects.observe(this.newDomainObject, path + "." + property, function (newValue) {
this.selectedElementProxy.element[property] = newValue;
this.updateSelectionStyle();
}.bind(this)));
}.bind(this));
};
FixedController.prototype.destroy = function () {
this.unsubscribe();
this.unlisten();
this.openmct.time.off("bounds", this.updateDisplayBounds);
this.openmct.selection.off("change", this.setSelection);
this.composition.off('add', this.onCompositionAdd, this);
this.composition.off('remove', this.onCompositionRemove, this);
};
/**
* A rate-limited digest function. Caps digests at 60Hz
* @private
@@ -340,31 +444,30 @@ define(
* @private
*/
FixedController.prototype.unsubscribe = function () {
this.subscriptions.forEach(function (unsubscribeFunc) {
Object.values(this.subscriptions).forEach(function (unsubscribeFunc) {
unsubscribeFunc();
});
this.subscriptions = [];
this.telemetryObjects = [];
this.subscriptions = {};
this.telemetryObjects = {};
};
/**
* Subscribe to all given domain objects
* Subscribe to the given domain object
* @private
* @param {object[]} objects Domain objects to subscribe to
* @returns {object[]} The provided objects, for chaining.
* @param {object} object Domain object to subscribe to
* @returns {object} The provided object, for chaining.
*/
FixedController.prototype.subscribeToObjects = function (objects) {
FixedController.prototype.subscribeToObject = function (object) {
var self = this;
var timeAPI = this.openmct.time;
var id = objectUtils.makeKeyString(object.identifier);
this.subscriptions[id] = self.openmct.telemetry.subscribe(object, function (datum) {
if (timeAPI.clock() !== undefined) {
self.updateView(object, datum);
}
}, {});
this.subscriptions = objects.map(function (object) {
return self.openmct.telemetry.subscribe(object, function (datum) {
if (timeAPI.clock() !== undefined) {
self.updateView(object, datum);
}
}, {});
});
return objects;
return object;
};
/**
@@ -416,23 +519,22 @@ define(
};
/**
* Request the last historical data point for the given domain objects
* @param {object[]} objects
* @returns {object[]} the provided objects for chaining.
* Request the last historical data point for the given domain object
* @param {object} object
* @returns {object} the provided object for chaining.
*/
FixedController.prototype.fetchHistoricalData = function (objects) {
FixedController.prototype.fetchHistoricalData = function (object) {
var bounds = this.openmct.time.bounds();
var self = this;
objects.forEach(function (object) {
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
.then(function (data) {
if (data.length > 0) {
self.updateView(object, data[data.length - 1]);
}
});
});
return objects;
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
.then(function (data) {
if (data.length > 0) {
self.updateView(object, data[data.length - 1]);
}
});
return object;
};
@@ -457,33 +559,25 @@ define(
};
FixedController.prototype.getTelemetry = function (domainObject) {
var newObject = domainObject.useCapability('adapter');
var self = this;
var id = objectUtils.makeKeyString(domainObject.identifier);
if (this.subscriptions.length > 0) {
this.unsubscribe();
if (this.subscriptions[id]) {
this.subscriptions[id]();
delete this.subscriptions[id];
}
delete this.telemetryObjects[id];
if (!this.openmct.telemetry.isTelemetryObject(domainObject)) {
return;
}
function filterForTelemetryObjects(objects) {
return objects.filter(function (object) {
return self.openmct.telemetry.isTelemetryObject(object);
});
}
// Initialize display
this.telemetryObjects[id] = domainObject;
this.setDisplayedValue(domainObject, "");
function initializeDisplay(objects) {
self.telemetryObjects = objects;
objects.forEach(function (object) {
// Initialize values
self.setDisplayedValue(object, "");
});
return objects;
}
return this.openmct.composition.get(newObject).load()
.then(filterForTelemetryObjects)
.then(initializeDisplay)
return Promise.resolve(domainObject)
.then(this.fetchHistoricalData)
.then(this.subscribeToObjects);
.then(this.subscribeToObject);
};
/**
@@ -580,12 +674,12 @@ define(
* Gets the selection context.
*
* @param elementProxy the element proxy
* @returns {object} the context object which includes elementProxy and toolbar
* @returns {object} the context object which includes elementProxy
*/
FixedController.prototype.getContext = function (elementProxy) {
return {
elementProxy: elementProxy,
toolbar: elementProxy
fixedController: this
};
};
@@ -608,6 +702,10 @@ define(
}
};
FixedController.prototype.mutate = function (path, value) {
this.openmct.objects.mutate(this.newDomainObject, path, value);
};
return FixedController;
}
);

View File

@@ -24,30 +24,34 @@ define(
[],
function () {
// Drag handle dimensions
var DRAG_HANDLE_SIZE = [6, 6];
/**
* Template-displayable drag handle for an element in fixed
* position mode.
*
* @param elementHandle the element handle
* @param configPath the configuration path of an element
* @param {Object} fixedControl the fixed controller
* @memberof platform/features/layout
* @constructor
*/
function FixedDragHandle(elementHandle, gridSize, update, commit) {
function FixedDragHandle(elementHandle, configPath, fixedControl) {
this.elementHandle = elementHandle;
this.gridSize = gridSize;
this.update = update;
this.commit = commit;
this.configPath = configPath;
this.fixedControl = fixedControl;
}
/**
* Get a CSS style to position this drag handle.
*
* @returns CSS style object (for `ng-style`)
* @memberof platform/features/layout.FixedDragHandle#
*/
FixedDragHandle.prototype.style = function () {
var gridSize = this.elementHandle.getGridSize();
// Adjust from grid to pixel coordinates
var x = this.elementHandle.x() * gridSize[0],
y = this.elementHandle.y() * gridSize[1];
@@ -75,23 +79,20 @@ define(
/**
* Continue a drag gesture; update x/y positions.
* @param {number[]} delta x/y pixel difference since drag
* started
*
* @param {number[]} delta x/y pixel difference since drag started
*/
FixedDragHandle.prototype.continueDrag = function (delta) {
var gridSize = this.elementHandle.getGridSize();
if (this.dragging) {
// Update x/y positions (snapping to grid)
this.elementHandle.x(
this.dragging.x + Math.round(delta[0] / gridSize[0])
);
this.elementHandle.y(
this.dragging.y + Math.round(delta[1] / gridSize[1])
);
// Invoke update callback
if (this.update) {
this.update();
}
var newX = this.dragging.x + Math.round(delta[0] / gridSize[0]);
var newY = this.dragging.y + Math.round(delta[1] / gridSize[1]);
this.elementHandle.x(Math.max(0, newX));
this.elementHandle.y(Math.max(0, newY));
this.fixedControl.updateSelectionStyle();
}
};
@@ -100,12 +101,8 @@ define(
* concludes to trigger commit of changes.
*/
FixedDragHandle.prototype.endDrag = function () {
// Clear cached state
this.dragging = undefined;
// Mark change as complete
if (this.commit) {
this.commit("Dragged handle.");
}
this.fixedControl.mutate(this.configPath, this.elementHandle.element);
};
return FixedDragHandle;

View File

@@ -78,29 +78,30 @@ define(
}
$scope.configuration = $scope.configuration || {};
$scope.configuration.panels =
$scope.configuration.panels || {};
$scope.configuration.panels = $scope.configuration.panels || {};
$scope.configuration.panels[id] = {
position: [
Math.floor(position.x / self.gridSize[0]),
Math.floor(position.y / self.gridSize[1])
],
dimensions: self.defaultDimensions()
};
self.openmct.objects.get(id).then(function (object) {
$scope.configuration.panels[id] = {
position: [
Math.floor(position.x / self.gridSize[0]),
Math.floor(position.y / self.gridSize[1])
],
dimensions: self.defaultDimensions(),
hasFrame: self.getDefaultFrame(object.type)
};
// Store the id so that the newly-dropped object
// gets selected during refresh composition
self.droppedIdToSelectAfterRefresh = id;
// Store the id so that the newly-dropped object
// gets selected during refresh composition
self.droppedIdToSelectAfterRefresh = id;
self.commit();
// Populate template-facing position for this id
self.rawPositions[id] = $scope.configuration.panels[id];
self.populatePosition(id);
refreshComposition();
});
// Mark change as persistable
if ($scope.commit) {
$scope.commit("Dropped a frame.");
}
// Populate template-facing position for this id
self.rawPositions[id] =
$scope.configuration.panels[id];
self.populatePosition(id);
// Layout may contain embedded views which will
// listen for drops, so call preventDefault() so
// that they can recognize that this event is handled.
@@ -157,10 +158,7 @@ define(
$scope.configuration.panels[self.activeDragId].dimensions =
self.rawPositions[self.activeDragId].dimensions;
// Mark this object as dirty to encourage persistence
if ($scope.commit) {
$scope.commit("Moved frame.");
}
self.commit();
};
// Sets the selectable object in response to the selection change event.
@@ -194,9 +192,22 @@ define(
$scope.$on("$destroy", function () {
openmct.selection.off("change", setSelection);
self.unlisten();
});
$scope.$on("mctDrop", handleDrop);
self.unlisten = self.$scope.domainObject.getCapability('mutation').listen(function (model) {
$scope.configuration = model.configuration.layout;
$scope.model = model;
var panels = $scope.configuration.panels;
Object.keys(panels).forEach(function (key) {
if (self.frames && self.frames.hasOwnProperty(key)) {
self.frames[key] = panels[key].hasFrame;
}
});
});
}
// Utility function to copy raw positions from configuration,
@@ -220,7 +231,6 @@ define(
*/
LayoutController.prototype.setFrames = function (ids) {
var panels = shallowCopy(this.$scope.configuration.panels || {}, ids);
this.frames = {};
this.$scope.composition.forEach(function (object) {
@@ -230,11 +240,22 @@ define(
if (panels[id].hasOwnProperty('hasFrame')) {
this.frames[id] = panels[id].hasFrame;
} else {
this.frames[id] = DEFAULT_HIDDEN_FRAME_TYPES.indexOf(object.getModel().type) === -1;
this.frames[id] = this.getDefaultFrame(object.getModel().type);
}
}, this);
};
/**
* Gets the default value for frame.
*
* @param type the domain object type
* @return {boolean} true if the object should have
* frame by default, false, otherwise
*/
LayoutController.prototype.getDefaultFrame = function (type) {
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
};
// Convert from { positions: ..., dimensions: ... } to an
// appropriate ng-style argument, to position frames.
LayoutController.prototype.convertPosition = function (raw) {
@@ -389,40 +410,6 @@ define(
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
};
/**
* Callback to show/hide the object frame.
*
* @param {string} id the object id
* @private
*/
LayoutController.prototype.toggleFrame = function (id, domainObject) {
var configuration = this.$scope.configuration;
if (!configuration.panels[id]) {
configuration.panels[id] = {};
}
this.frames[id] = configuration.panels[id].hasFrame = !this.frames[id];
var selection = this.openmct.selection.get();
selection[0].context.toolbar = this.getToolbar(id, domainObject);
this.openmct.selection.select(selection); // reselect so toolbar updates
};
/**
* Gets the toolbar object for the given domain object.
*
* @param id the domain object id
* @param domainObject the domain object
* @returns {object}
* @private
*/
LayoutController.prototype.getToolbar = function (id, domainObject) {
var toolbarObj = {};
toolbarObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id, domainObject);
return toolbarObj;
};
/**
* Bypasses selection if drag is in progress.
*
@@ -497,17 +484,25 @@ define(
* Gets the selection context.
*
* @param domainObject the domain object
* @returns {object} the context object which includes
* item, oldItem and toolbar
* @returns {object} the context object which includes item and oldItem
*/
LayoutController.prototype.getContext = function (domainObject, toolbar) {
LayoutController.prototype.getContext = function (domainObject) {
return {
item: domainObject.useCapability('adapter'),
oldItem: domainObject,
toolbar: toolbar ? this.getToolbar(domainObject.getId(), domainObject) : undefined
oldItem: domainObject
};
};
LayoutController.prototype.commit = function () {
var model = this.$scope.model;
model.configuration = model.configuration || {};
model.configuration.layout = this.$scope.configuration;
this.$scope.domainObject.useCapability('mutation', function () {
return model;
});
};
/**
* Selects a newly-dropped object.
*

View File

@@ -21,23 +21,12 @@
*****************************************************************************/
define([
'zepto'
'zepto',
'../../../commonUI/general/src/services/Overlay'
], function (
$
$,
Overlay
) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
/**
* MCT Trigger Modal is intended for use in only one location: inside the
* object-header to allow views in a layout to be popped out in a modal.
@@ -50,9 +39,11 @@ define([
* descendent of a `.frame` element.
*/
function MCTTriggerModal($document) {
var document = $document[0];
function link($scope, $element) {
var actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
var frame = $element.parent();
for (var i = 0; i < 10; i++) {
@@ -67,61 +58,39 @@ define([
}
frame = frame[0];
var layoutContainer = frame.parentElement,
isOpen = false,
toggleOverlay,
overlay,
closeButton,
doneButton,
blocker,
overlayContainer;
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
document.body.appendChild(overlay);
layoutContainer.removeChild(frame);
overlayContainer.appendChild(frame);
}
var layoutContainer = frame.parentElement;
function closeOverlay() {
$(frame).addClass('frame frame-template');
overlayContainer.removeChild(frame);
layoutContainer.appendChild(frame);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
}
var notebookButton = notebookAction ?
[
{
class: 'icon-notebook new-notebook-entry',
title: 'New Notebook Entry',
clickHandler: function (event) {
event.stopPropagation();
notebookAction.perform();
}
}
] : [];
toggleOverlay = function () {
if (!isOpen) {
openOverlay();
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
var overlayService = new Overlay ({
$document: $document,
$scope: $scope,
$element: frame,
overlayWillMount: function () {
$(frame).removeClass('frame frame-template');
layoutContainer.removeChild(frame);
},
overlayDidUnmount: function () {
$(frame).addClass('frame frame-template');
layoutContainer.appendChild(frame);
},
browseBarButtons: notebookButton
});
$element.on('click', toggleOverlay);
$element.on('click', overlayService.toggleOverlay);
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
$element.off('click', overlayService.toggleOverlay);
});
}

View File

@@ -53,12 +53,6 @@ define(
*/
proxy.fill = new AccessorMutator(element, 'fill');
//Expose x,y, width and height for editing
proxy.editWidth = new AccessorMutator(element, 'width');
proxy.editHeight = new AccessorMutator(element, 'height');
proxy.editX = new AccessorMutator(element, 'x');
proxy.editY = new AccessorMutator(element, 'y');
return proxy;
}

View File

@@ -71,13 +71,6 @@ define(
*/
this.gridSize = gridSize || [1,1]; //Ensure a reasonable default
this.resizeHandles = [new ResizeHandle(
this.element,
this.getMinWidth(),
this.getMinHeight(),
this.getGridSize()
)];
/**
* Get and/or set the x position of this element.
* Units are in fixed position grid space.
@@ -123,15 +116,16 @@ define(
this.height = new AccessorMutator(element, 'height');
this.useGrid = new UnitAccessorMutator(this);
this.index = index;
this.elements = elements;
this.resizeHandles = [new ResizeHandle(this, this.element)];
}
/**
* Change the display order of this element.
* @param {string} o where to move this element;
* one of "top", "up", "down", or "bottom"
* @return {Array} the full array of elements
*/
ElementProxy.prototype.order = function (o) {
var index = this.index,
@@ -152,16 +146,8 @@ define(
// anyway, but be consistent)
this.index = desired;
}
};
/**
* Remove this element from the fixed position view.
*/
ElementProxy.prototype.remove = function () {
var index = this.index;
if (this.elements[index] === this.element) {
this.elements.splice(index, 1);
}
return elements;
};
/**
@@ -208,7 +194,6 @@ define(
*/
ElementProxy.prototype.getMinWidth = function () {
return Math.ceil(MIN_WIDTH / this.getGridSize()[0]);
};
/**

View File

@@ -50,12 +50,6 @@ define(
*/
proxy.url = new AccessorMutator(element, 'url');
//Expose x,y, width and height properties for editing
proxy.editWidth = new AccessorMutator(element, 'width');
proxy.editHeight = new AccessorMutator(element, 'height');
proxy.editX = new AccessorMutator(element, 'x');
proxy.editY = new AccessorMutator(element, 'y');
return proxy;
}

View File

@@ -32,19 +32,18 @@ define(
* @constructor
* @param element the line element
* @param {string} xProperty field which stores x position
* @param {string} yProperty field which stores x position
* @param {string} yProperty field which stores y position
* @param {string} xOther field which stores x of other end
* @param {string} yOther field which stores y of other end
* @param {number[]} gridSize the current layout grid size in [x,y] from
* @implements {platform/features/layout.ElementHandle}
*/
function LineHandle(element, xProperty, yProperty, xOther, yOther, gridSize) {
function LineHandle(element, elementProxy, xProperty, yProperty, xOther, yOther) {
this.elementProxy = elementProxy;
this.element = element;
this.xProperty = xProperty;
this.yProperty = yProperty;
this.xOther = xOther;
this.yOther = yOther;
this.gridSize = gridSize;
}
LineHandle.prototype.x = function (value) {
@@ -86,7 +85,7 @@ define(
};
LineHandle.prototype.getGridSize = function () {
return this.gridSize;
return this.elementProxy.getGridSize();
};
return LineHandle;

View File

@@ -39,10 +39,24 @@ define(
function LineProxy(element, index, elements, gridSize) {
var proxy = new ElementProxy(element, index, elements, gridSize),
handles = [
new LineHandle(element, 'x', 'y', 'x2', 'y2', proxy.getGridSize()),
new LineHandle(element, 'x2', 'y2', 'x', 'y', proxy.getGridSize())
new LineHandle(element, proxy, 'x', 'y', 'x2', 'y2'),
new LineHandle(element, proxy, 'x2', 'y2', 'x', 'y')
];
/**
* Gets style specific to line proxy.
*/
proxy.getStyle = function () {
var layoutGridSize = proxy.getGridSize();
return {
left: (layoutGridSize[0] * proxy.x()) + 'px',
top: (layoutGridSize[1] * proxy.y()) + 'px',
width: (layoutGridSize[0] * proxy.width()) + 'px',
height: (layoutGridSize[1] * proxy.height()) + 'px'
};
};
/**
* Get the top-left x coordinate, in grid space, of
* this line's bounding box.
@@ -149,12 +163,6 @@ define(
return handles;
};
// Expose endpoint coordinates for editing
proxy.editX1 = new AccessorMutator(element, 'x');
proxy.editY1 = new AccessorMutator(element, 'y');
proxy.editX2 = new AccessorMutator(element, 'x2');
proxy.editY2 = new AccessorMutator(element, 'y2');
return proxy;
}

View File

@@ -35,21 +35,16 @@ define(
* @memberof platform/features/layout
* @constructor
*/
function ResizeHandle(element, minWidth, minHeight, gridSize) {
function ResizeHandle(elementProxy, element) {
this.elementProxy = elementProxy;
this.element = element;
// Ensure reasonable defaults
this.minWidth = minWidth || 0;
this.minHeight = minHeight || 0;
this.gridSize = gridSize;
}
ResizeHandle.prototype.x = function (value) {
var element = this.element;
if (arguments.length > 0) {
element.width = Math.max(
this.minWidth,
this.elementProxy.getMinWidth(),
value - element.x
);
}
@@ -60,7 +55,7 @@ define(
var element = this.element;
if (arguments.length > 0) {
element.height = Math.max(
this.minHeight,
this.elementProxy.getMinHeight(),
value - element.y
);
}
@@ -68,7 +63,7 @@ define(
};
ResizeHandle.prototype.getGridSize = function () {
return this.gridSize;
return this.elementProxy.getGridSize();
};
return ResizeHandle;

View File

@@ -24,9 +24,6 @@ define(
['./TextProxy'],
function (TextProxy) {
// Method names to expose from this proxy
var HIDE = 'hideTitle', SHOW = 'showTitle';
/**
* Selection proxy for telemetry elements in a fixed position view.
*
@@ -45,24 +42,9 @@ define(
function TelemetryProxy(element, index, elements, gridSize) {
var proxy = new TextProxy(element, index, elements, gridSize);
// Toggle the visibility of the title
function toggle() {
// Toggle the state
element.titled = !element.titled;
// Change which method is exposed, to influence
// which button is shown in the toolbar
delete proxy[SHOW];
delete proxy[HIDE];
proxy[element.titled ? HIDE : SHOW] = toggle;
}
// Expose the domain object identifier
proxy.id = element.id;
// Expose initial toggle
proxy[element.titled ? HIDE : SHOW] = toggle;
// Don't expose text configuration
delete proxy.text;

View File

@@ -53,22 +53,14 @@ define(
mockTimeSystem,
mockLimitEvaluator,
mockSelection,
mockObjects,
mockNewDomainObject,
unlistenFunc,
$element = [],
selectable = [],
controller;
// Utility function; find a watch for a given expression
function findWatch(expr) {
var watch;
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
watch = call.args[1];
}
});
return watch;
}
// As above, but for $on calls
// Utility function; find a $on calls for a given expression.
function findOn(expr) {
var on;
mockScope.$on.calls.forEach(function (call) {
@@ -82,7 +74,8 @@ define(
function makeMockDomainObject(id) {
return {
identifier: {
key: "domainObject-" + id
key: "domainObject-" + id,
namespace: ""
},
name: "Point " + id
};
@@ -110,11 +103,6 @@ define(
return "Formatted " + valueMetadata.value;
});
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getModel', 'getCapability', 'useCapability']
);
mockHandle = jasmine.createSpyObj(
'subscription',
[
@@ -172,16 +160,14 @@ define(
]};
mockChildren = testModel.composition.map(makeMockDomainObject);
mockCompositionCollection = jasmine.createSpyObj('compositionCollection',
[
'load'
]
);
mockCompositionAPI = jasmine.createSpyObj('composition',
[
'get'
]
);
mockCompositionCollection = jasmine.createSpyObj('compositionCollection', [
'load',
'on',
'off'
]);
mockCompositionAPI = jasmine.createSpyObj('composition', [
'get'
]);
mockCompositionAPI.get.andReturn(mockCompositionCollection);
mockCompositionCollection.load.andReturn(
Promise.resolve(mockChildren)
@@ -190,6 +176,24 @@ define(
mockScope.model = testModel;
mockScope.configuration = testConfiguration;
mockNewDomainObject = jasmine.createSpyObj("newDomainObject", [
'layoutGrid',
'configuration',
'composition'
]);
mockNewDomainObject.layoutGrid = testGrid;
mockNewDomainObject.configuration = {
'fixed-display': testConfiguration
};
mockNewDomainObject.composition = ['a', 'b', 'c'];
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getModel', 'getCapability', 'useCapability']
);
mockDomainObject.useCapability.andReturn(mockNewDomainObject);
mockScope.domainObject = mockDomainObject;
selectable[0] = {
context: {
oldItem: mockDomainObject
@@ -203,11 +207,19 @@ define(
]);
mockSelection.get.andReturn([]);
unlistenFunc = jasmine.createSpy("unlisten");
mockObjects = jasmine.createSpyObj('objects', [
'observe',
'get'
]);
mockObjects.observe.andReturn(unlistenFunc);
mockOpenMCT = {
time: mockConductor,
telemetry: mockTelemetryAPI,
composition: mockCompositionAPI,
selection: mockSelection
selection: mockSelection,
objects: mockObjects
};
$element = $('<div></div>');
@@ -251,76 +263,60 @@ define(
mockOpenMCT,
$element
);
findWatch("model.layoutGrid")(testModel.layoutGrid);
spyOn(controller, "mutate");
});
it("subscribes when a domain object is available", function () {
var dunzo = false;
it("subscribes a domain object", function () {
var object = makeMockDomainObject("mock");
var done = false;
mockScope.domainObject = mockDomainObject;
findWatch("domainObject")(mockDomainObject).then(function () {
dunzo = true;
controller.getTelemetry(object).then(function () {
done = true;
});
waitsFor(function () {
return dunzo;
}, "Telemetry fetched", 200);
return done;
});
runs(function () {
mockChildren.forEach(function (child) {
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(
child,
jasmine.any(Function),
jasmine.any(Object)
);
});
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(
object,
jasmine.any(Function),
jasmine.any(Object)
);
});
});
it("releases subscriptions when domain objects change", function () {
var dunzo = false;
it("releases subscription when a domain objects is removed", function () {
var done = false;
var unsubscribe = jasmine.createSpy('unsubscribe');
var object = makeMockDomainObject("mock");
mockTelemetryAPI.subscribe.andReturn(unsubscribe);
mockScope.domainObject = mockDomainObject;
findWatch("domainObject")(mockDomainObject).then(function () {
dunzo = true;
controller.getTelemetry(object).then(function () {
done = true;
});
waitsFor(function () {
return dunzo;
}, "Telemetry fetched", 200);
return done;
});
runs(function () {
expect(unsubscribe).not.toHaveBeenCalled();
dunzo = false;
findWatch("domainObject")(mockDomainObject).then(function () {
dunzo = true;
});
controller.onCompositionRemove(object.identifier);
waitsFor(function () {
return dunzo;
}, "Telemetry fetched", 200);
runs(function () {
expect(unsubscribe.calls.length).toBe(mockChildren.length);
return unsubscribe.calls.length > 0;
});
runs(function () {
expect(unsubscribe).toHaveBeenCalled();
});
});
});
it("exposes visible elements based on configuration", function () {
var elements;
var elements = controller.getElements();
mockScope.model = testModel;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
elements = controller.getElements();
expect(elements.length).toEqual(3);
expect(elements[0].id).toEqual('a');
expect(elements[1].id).toEqual('b');
@@ -328,9 +324,6 @@ define(
});
it("allows elements to be selected", function () {
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
@@ -338,12 +331,7 @@ define(
});
it("allows selection retrieval", function () {
var elements;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
elements = controller.getElements();
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
@@ -351,16 +339,10 @@ define(
});
it("selects the parent view when selected element is removed", function () {
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
elements[1].remove();
testModel.modified = 2;
findWatch("model.modified")(testModel.modified);
controller.remove(elements[1]);
expect($element[0].click).toHaveBeenCalled();
});
@@ -368,21 +350,13 @@ define(
it("retains selections during refresh", function () {
// Get elements; remove one of them; trigger refresh.
// Same element (at least by index) should still be selected.
var elements;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
elements = controller.getElements();
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
expect(controller.getSelectedElement()).toEqual(elements[1]);
elements[2].remove();
testModel.modified = 2;
findWatch("model.modified")(testModel.modified);
controller.remove(elements[2]);
elements = controller.getElements();
// Verify removal, as test assumes this
@@ -408,7 +382,7 @@ define(
controller.elementProxiesById['12345'] = [testElement];
controller.elementProxies = [testElement];
controller.subscribeToObjects([telemetryObject]);
controller.subscribeToObject(telemetryObject);
mockTelemetryAPI.subscribe.mostRecentCall.args[1](mockTelemetry);
waitsFor(function () {
@@ -426,18 +400,13 @@ define(
});
it("updates elements styles when grid size changes", function () {
var originalLeft;
// Grid size is initially set to testGrid which is [123, 456]
var originalLeft = controller.getElements()[0].style.left;
mockScope.domainObject = mockDomainObject;
mockScope.model = testModel;
findWatch("domainObject")(mockDomainObject);
findWatch("model.modified")(1);
findWatch("model.composition")(mockScope.model.composition);
findWatch("model.layoutGrid")([10, 10]);
originalLeft = controller.getElements()[0].style.left;
findWatch("model.layoutGrid")([20, 20]);
expect(controller.getElements()[0].style.left)
.not.toEqual(originalLeft);
// Change the grid size
controller.updateElementPositions([20, 20]);
expect(controller.getElements()[0].style.left).not.toEqual(originalLeft);
});
it("listens for drop events", function () {
@@ -457,6 +426,9 @@ define(
// Notify that a drop occurred
testModel.composition.push('d');
mockObjects.get.andReturn(Promise.resolve([]));
findOn('mctDrop')(
mockEvent,
'd',
@@ -468,11 +440,6 @@ define(
// ...and prevented default...
expect(mockEvent.preventDefault).toHaveBeenCalled();
// Should have triggered commit (provided by
// EditRepresenter) with some message.
expect(mockScope.commit)
.toHaveBeenCalledWith(jasmine.any(String));
});
it("ignores drops when default has been prevented", function () {
@@ -492,52 +459,35 @@ define(
});
it("unsubscribes when destroyed", function () {
var dunzo = false;
var done = false;
var unsubscribe = jasmine.createSpy('unsubscribe');
var object = makeMockDomainObject("mock");
mockTelemetryAPI.subscribe.andReturn(unsubscribe);
mockScope.domainObject = mockDomainObject;
findWatch("domainObject")(mockDomainObject).then(function () {
dunzo = true;
controller.getTelemetry(object).then(function () {
done = true;
});
waitsFor(function () {
return dunzo;
}, "Telemetry fetched", 200);
return done;
});
runs(function () {
expect(unsubscribe).not.toHaveBeenCalled();
// Destroy the scope
findOn('$destroy')();
//Check that the same unsubscribe function returned by the
expect(unsubscribe.calls.length).toBe(mockChildren.length);
expect(unsubscribe).toHaveBeenCalled();
});
});
it("exposes its grid size", function () {
findWatch('model.layoutGrid')(testGrid);
// Template needs to be able to pass this into line
// elements to size SVGs appropriately
expect(controller.getGridSize()).toEqual(testGrid);
});
it("exposes a view-level selection proxy", function () {
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var selection = mockOpenMCT.selection.select.mostRecentCall.args[0];
expect(mockOpenMCT.selection.select).toHaveBeenCalled();
expect(selection.context.viewProxy).toBeDefined();
});
it("exposes drag handles", function () {
var handles;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
@@ -556,9 +506,6 @@ define(
});
it("exposes a move handle", function () {
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
@@ -573,10 +520,6 @@ define(
it("updates selection style during drag", function () {
var oldStyle;
testModel.modified = 1;
findWatch("model.modified")(testModel.modified);
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
@@ -677,7 +620,7 @@ define(
value: testValue
}]));
controller.fetchHistoricalData([mockTelemetryObject]);
controller.fetchHistoricalData(mockTelemetryObject);
waitsFor(function () {
return controller.digesting === false;

View File

@@ -28,8 +28,8 @@ define(
describe("A fixed position drag handle", function () {
var mockElementHandle,
mockUpdate,
mockCommit,
mockConfigPath,
mockFixedControl,
handle;
beforeEach(function () {
@@ -37,18 +37,23 @@ define(
'elementHandle',
['x', 'y','getGridSize']
);
mockUpdate = jasmine.createSpy('update');
mockCommit = jasmine.createSpy('commit');
mockElementHandle.x.andReturn(6);
mockElementHandle.y.andReturn(8);
mockElementHandle.getGridSize.andReturn(TEST_GRID_SIZE);
mockFixedControl = jasmine.createSpyObj(
'fixedControl',
['updateSelectionStyle', 'mutate']
);
mockFixedControl.updateSelectionStyle.andReturn();
mockFixedControl.mutate.andReturn();
mockConfigPath = jasmine.createSpy('configPath');
handle = new FixedDragHandle(
mockElementHandle,
TEST_GRID_SIZE,
mockUpdate,
mockCommit
mockConfigPath,
mockFixedControl
);
});
@@ -74,13 +79,12 @@ define(
expect(mockElementHandle.x).toHaveBeenCalledWith(5);
expect(mockElementHandle.y).toHaveBeenCalledWith(7);
// Should have called update once per continueDrag
expect(mockUpdate.calls.length).toEqual(2);
// Should have called updateSelectionStyle once per continueDrag
expect(mockFixedControl.updateSelectionStyle.calls.length).toEqual(2);
// Finally, ending drag should commit
expect(mockCommit).not.toHaveBeenCalled();
// Finally, ending drag should mutate
handle.endDrag();
expect(mockCommit).toHaveBeenCalled();
expect(mockFixedControl.mutate).toHaveBeenCalled();
});
});

View File

@@ -42,6 +42,8 @@ define(
mockOpenMCT,
mockSelection,
mockDomainObjectCapability,
mockObjects,
unlistenFunc,
$element = [],
selectable = [];
@@ -77,14 +79,15 @@ define(
if (param === 'composition') {
return id !== 'b';
}
}
},
type: "testType"
};
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$watch", "$watchCollection", "$on", "commit"]
["$watch", "$watchCollection", "$on"]
);
mockEvent = jasmine.createSpyObj(
'event',
@@ -104,9 +107,13 @@ define(
}
}
};
unlistenFunc = jasmine.createSpy("unlisten");
mockDomainObjectCapability = jasmine.createSpyObj('capability',
['inEditContext']
['inEditContext', 'listen']
);
mockDomainObjectCapability.listen.andReturn(unlistenFunc);
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockScope.domainObject = mockDomainObject("mockDomainObject");
@@ -126,8 +133,14 @@ define(
'get'
]);
mockSelection.get.andReturn(selectable);
mockObjects = jasmine.createSpyObj('objects', [
'get'
]);
mockObjects.get.andReturn(mockPromise(mockDomainObject("mockObject")));
mockOpenMCT = {
selection: mockSelection
selection: mockSelection,
objects: mockObjects
};
$element = $('<div></div>');
@@ -138,6 +151,7 @@ define(
controller = new LayoutController(mockScope, $element, mockOpenMCT);
spyOn(controller, "layoutPanels").andCallThrough();
spyOn(controller, "commit");
jasmine.Clock.useMock();
});
@@ -270,10 +284,7 @@ define(
controller.continueDrag([100, 100]);
controller.endDrag();
// Should have triggered commit (provided by
// EditRepresenter) with some message.
expect(mockScope.commit)
.toHaveBeenCalledWith(jasmine.any(String));
expect(controller.commit).toHaveBeenCalled();
});
it("listens for drop events", function () {
@@ -296,11 +307,7 @@ define(
);
expect(testConfiguration.panels.d).toBeDefined();
expect(mockEvent.preventDefault).toHaveBeenCalled();
// Should have triggered commit (provided by
// EditRepresenter) with some message.
expect(mockScope.commit)
.toHaveBeenCalledWith(jasmine.any(String));
expect(controller.commit).toHaveBeenCalled();
});
it("ignores drops when default has been prevented", function () {
@@ -340,13 +347,17 @@ define(
testModel.layoutGrid = [1, 1];
mockScope.$watch.calls[0].args[1](testModel.layoutGrid);
// Add a new object to the composition
mockComposition = ["a", "b", "c", "d"];
mockCompositionObjects = mockComposition.map(mockDomainObject);
mockCompositionCapability = mockPromise(mockCompositionObjects);
// Notify that a drop occurred
mockScope.$on.mostRecentCall.args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
mockScope.$watch.calls[0].args[1](['d']);
style = controller.getFrameStyle("d");
@@ -415,30 +426,6 @@ define(
expect(controller.hasFrame(mockCompositionObjects[1])).toBe(false);
});
it("hides frame when selected object has frame ", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var toolbarObj = controller.getToolbar(childObj.getId(), childObj);
expect(controller.hasFrame(childObj)).toBe(true);
expect(toolbarObj.hideFrame).toBeDefined();
expect(toolbarObj.hideFrame).toEqual(jasmine.any(Function));
});
it("shows frame when selected object has no frame", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[1];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
var toolbarObj = controller.getToolbar(childObj.getId(), childObj);
expect(controller.hasFrame(childObj)).toBe(false);
expect(toolbarObj.showFrame).toBeDefined();
expect(toolbarObj.showFrame).toEqual(jasmine.any(Function));
});
it("selects the parent object when selected object is removed", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];

View File

@@ -52,6 +52,12 @@ define([
beforeEach(function () {
$scope = jasmine.createSpyObj('$scope', ['$on']);
$scope.domainObject = { getCapability: function () {
return { getActions: function () {
return [];
}};
}};
$element = jasmine.createSpyObj('$element', [
'parent',
'remove',

View File

@@ -53,11 +53,6 @@ define(
});
});
it("allows elements to be removed", function () {
proxy.remove();
expect(testElements).toEqual([{}, {}, {}]);
});
it("allows order to be changed", function () {
proxy.order("down");
expect(testElements).toEqual([{}, testElement, {}, {}]);

View File

@@ -26,7 +26,9 @@ define(
describe("A fixed position drag handle", function () {
var testElement,
handle;
mockElementProxy,
handle,
TEST_GRID_SIZE = [45, 21];
beforeEach(function () {
testElement = {
@@ -36,8 +38,10 @@ define(
y2: 11,
useGrid: true
};
mockElementProxy = jasmine.createSpyObj('elementProxy', ['getGridSize']);
mockElementProxy.getGridSize.andReturn(TEST_GRID_SIZE);
handle = new LineHandle(testElement, 'x', 'y', 'x2', 'y2', [45,21]);
handle = new LineHandle(testElement, mockElementProxy, 'x', 'y', 'x2', 'y2');
});
it("provides x/y grid coordinates for its corner", function () {
@@ -69,7 +73,7 @@ define(
});
it("returns the correct grid size", function () {
expect(handle.getGridSize()).toEqual([45,21]);
expect(handle.getGridSize()).toEqual(TEST_GRID_SIZE);
});
});

View File

@@ -63,13 +63,13 @@ define(
it("adjusts both ends when mutating x", function () {
var proxy = new LineProxy(diagonal);
proxy.x(6);
expect(diagonal).toEqual({ x: 6, y: 8, x2: 8, y2: 11, useGrid: true });
expect(diagonal).toEqual({ x: 6, y: 8, x2: 8, y2: 11});
});
it("adjusts both ends when mutating y", function () {
var proxy = new LineProxy(diagonal);
proxy.y(6);
expect(diagonal).toEqual({ x: 3, y: 6, x2: 5, y2: 9, useGrid: true });
expect(diagonal).toEqual({ x: 3, y: 6, x2: 5, y2: 9});
});
it("provides internal positions for SVG lines", function () {

View File

@@ -25,10 +25,12 @@ define(
function (ResizeHandle) {
var TEST_MIN_WIDTH = 4,
TEST_MIN_HEIGHT = 2;
TEST_MIN_HEIGHT = 2,
TEST_GRID_SIZE = [34, 81];
describe("A fixed position drag handle", function () {
var testElement,
mockElementProxy,
handle;
beforeEach(function () {
@@ -39,12 +41,18 @@ define(
height: 36,
useGrid: true
};
mockElementProxy = jasmine.createSpyObj('elementProxy', [
'getGridSize',
'getMinWidth',
'getMinHeight'
]);
mockElementProxy.getGridSize.andReturn(TEST_GRID_SIZE);
mockElementProxy.getMinWidth.andReturn(TEST_MIN_WIDTH);
mockElementProxy.getMinHeight.andReturn(TEST_MIN_HEIGHT);
handle = new ResizeHandle(
testElement,
TEST_MIN_WIDTH,
TEST_MIN_HEIGHT,
[34,81]
mockElementProxy,
testElement
);
});
@@ -77,7 +85,7 @@ define(
});
it("returns the correct grid size", function () {
expect(handle.getGridSize()).toEqual([34,81]);
expect(handle.getGridSize()).toEqual(TEST_GRID_SIZE);
});
});

View File

@@ -49,27 +49,6 @@ define(
it("exposes the element's id", function () {
expect(proxy.id).toEqual('test-id');
});
it("allows title to be shown/hidden", function () {
// Initially, only showTitle and hideTitle are available
expect(proxy.hideTitle).toBeUndefined();
proxy.showTitle();
// Should have set titled state
expect(testElement.titled).toBeTruthy();
// Should also have changed methods available
expect(proxy.showTitle).toBeUndefined();
proxy.hideTitle();
// Should have cleared titled state
expect(testElement.titled).toBeFalsy();
// Available methods should have changed again
expect(proxy.hideTitle).toBeUndefined();
proxy.showTitle();
});
});
}
);

View File

@@ -32,6 +32,11 @@ define(function () {
this.utc = formatService.getFormat('utc');
//Trigger digestive cycle with $apply to update list view
setTimeout(function () {
$scope.$apply();
});
$scope.$on('$destroy', function () {
unlisten();
});

View File

@@ -104,7 +104,7 @@ define(
scope = jasmine.createSpyObj(
"$scope",
["$on"]
["$on", "$apply"]
);
scope.domainObject = domainObject;
@@ -146,6 +146,7 @@ define(
runs(function () {
expect(scope.children.length).toEqual(0);
});
expect(scope.$apply).toHaveBeenCalled();
});
it("releases listeners on $destroy", function () {
expect(scope.$on).toHaveBeenCalledWith('$destroy', jasmine.any(Function));

View File

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

View File

@@ -0,0 +1,308 @@
/*****************************************************************************
* 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.
*****************************************************************************/
.w-notebook {
font-size: 0.8rem;
overflow: hidden;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: auto;
height: auto;
}
.notebook-view-controls.l-flex-row {
> * {
// filter and sort selects
&:not(:first-child) { margin-left: $interiorMargin; }
}
}
.l-notebook-drag-area {
padding: 10px;
font-style: italic;
cursor: pointer;
&:before { margin-right: 7px !important; }
.label {
@include ellipsize();
}
}
.frame {
.icon-notebook {
margin-right: 5px;
}
}
.overlay.l-dialog .title{
white-space: normal;
}
.w-notebook-entries {
padding-right: $interiorMarginSm;
position: relative;
overflow-x: hidden;
overflow-y: scroll;
.t-entries-list {
}
}
.l-notebook-entry {
$p: $interiorMarginSm;
box-sizing: border-box;
margin-bottom: $p;
padding: $p $interiorMargin;
.s-notebook-entry-time,
.s-notebook-entry-text,
.notebook-entry-delete {
padding-top: $p;
padding-bottom: $p;
}
.s-notebook-entry-time {
border: 1px solid transparent; // Needed to maintain vertical alignment with s-notebook-entry-text
}
.l-notebook-entry-content{
.s-notebook-entry-text {
// Contenteditable div that holds text
min-height: 24px; // Needed in Firefox when field is blank
white-space: pre-wrap;
}
.entry-embeds{
flex-wrap: wrap;
}
.snap-thumb {
cursor: pointer;
}
}
}
.l-entry-embed {
$m: $interiorMarginSm;
position: relative;
margin: $m $m 0 0;
padding: $interiorMarginSm;
&.has-snapshot {
&:before {
position: absolute;
text-shadow: rgba(black, 0.7) 0 1px 5px;
z-index: 2;
}
}
.snap-thumb {
$d: 50px;
width: $d;
height: $d;
border-radius: 5px;
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
.embed-info {
margin-left: $interiorMargin;
.embed-title {
font-weight: bold;
}
}
}
.t-contents,
.snap-annotation {
// Todo: don't write this to t-contents, add a l- class
overflow: hidden;
}
.notebook-filters {
.select {
margin-left: $interiorMargin;
}
}
/********************************************* MOBILE */
body.mobile {
// Hide the start entry area, and disable ability to edit or delete an entry in mobile context
.l-notebook-drag-area {
display: none;
}
.l-notebook-entry {
pointer-events: none;
}
&.phone.portrait {
.w-notebook-entry-time-and-content {
flex-direction: column !important;
}
.s-notebook-entry-time,
.notebook-entry-delete {
padding-top: 0;
padding-bottom: 0;
}
}
}
body.phone.portrait {
.l-notebook-head.l-flex-row {
flex-direction: column !important;
> * {
&:not(:first-child) { margin-top: $interiorMargin; }
}
}
}
/********************************************* PAINTERRO OVERRIDES */
.annotation-dialog .abs.editor {
border-radius: 0;
}
#snap-annotation {
display: flex;
flex-direction: column;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
#snap-annotation-wrapper,
#snap-annotation-bar {
position: relative;
top: auto; right: auto; bottom: auto; left: auto;
}
#snap-annotation-wrapper {
order: 2;
flex: 10 0 auto;
}
#snap-annotation-bar {
order: 1;
flex: 0 0 auto;
height: auto;
background-color: transparent !important;
margin-bottom: $interiorMargin;
> div,
> div > span,
.ptro-icon-btn,
.ptro-named-btn,
.ptro-color-btn,
.ptro-bordered-btn,
.ptro-tool-ctl-name,
.ptro-color-btn,
.tool-controls,
.ptro-input {
// Lot of resets for crappy CSS in Painterro
&:first-child {
margin-left: 0 !important;
}
$h: $btnToolbarH;
display: inline-block;
font-family: inherit;
font-size: auto;
height: $h !important;
margin: 0 0 0 5px;
position: relative;
width: auto !important;
line-height: $h !important;
top: auto;
right: auto;
bottom: auto;
left: auto;
vertical-align: top;
}
.ptro-tool-ctl-name {
border-radius: 0;
background: none;
top: auto;
font-family: inherit;
padding: 0;
}
.ptro-color-btn {
width: $btnToolbarH !important;
}
.ptro-icon-btn,
.ptro-named-btn {
// .s-button class is added via JS in AnnotateSnapshot.js
// TODO: redo this so that we don't need to use Zepto and JS
i {
font-size: 1.25em !important;
}
}
.tool-controls {
font-size: 0.8rem !important;
}
.ptro-info,
.ptro-btn-color-checkers-bar,
*[title="Font name"],
*[title="Stroke color"],
*[title="Stroke width"],
*[data-id="fontName"],
*[data-id="fontStrokeSize"],
*[data-id="stroke"] {
display: none;
}
}
/********************************************* NO IDEA WHAT THERE ARE APPLYING TO */
.context-available {
outline: none;
}
.menu-element.menu-view{
z-index: 999;
}
.overlay.l-dialog .abs.editor {
padding-right: 0;
}
/*
.overlay.l-dialog .outer-holder.annotation-dialog{
width: 90%;
height: 90%;
}
*/
/*
.snap-annotation-wrapper{
padding-top: 40px;
}
.t-console {
// Temp console-like reporting element
max-height: 200px;
box-sizing: border-box;
padding: 5px;
}
*/

View File

@@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.l-notebook-drag-area {
border: 1px dashed rgba($colorKey, 0.7);
border-radius: $controlCr;
color: rgba($colorBodyFg, 0.7);
&:hover {
background: rgba($colorKey, 0.2);
color: $colorBodyFg;
}
&.drag-active{
border-color: $colorKey;
}
}
.s-notebook-entry {
background-color: rgba($colorBodyFg, 0.1);
border-radius: $basicCr;
&:hover {
background-color: rgba($colorBodyFg, 0.2);
}
.s-notebook-entry-time {
color: rgba($colorBodyFg, 0.5);
}
}
.l-entry-embed {
border-radius: $controlCr;
background-color: rgba($colorBodyFg, 0.1);
&.has-snapshot {
&:before {
color: $colorBodyFg;
}
}
}
.s-snapshot-datetime {
color: rgba($colorBodyFg, 0.4);
font-size: 0.8em;
}
.snap-thumb {
border: 1px solid $colorInteriorBorder;
}
.s-status-taking-snapshot,
.overlay.snapshot {
// Applied to an object view when it's in the process of being snapshotted
background: $colorBodyBg;
}
/********************************************* PAINTERRO OVERRIDES */
.ptro-wrapper {
background: rgba($colorBodyBg, 0.3) !important;
}
#snap-annotation-bar {
.tool-controls {
color: $colorBodyFg !important;
}
}
.s-button.ptro-color-active-control {
background: $colorBtnMajorBg !important;
color: $colorBtnMajorFg !important;
&:hover {
background: $colorBtnMajorBgHov !important;
color: $colorBtnMajorFgHov !important;
}
}

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