Made changes to error reporting

This commit is contained in:
Andrew Henry 2015-11-04 22:07:26 -08:00
commit 8e85675732
101 changed files with 7959 additions and 3303 deletions

View File

@ -6,6 +6,7 @@
"platform/commonUI/browse",
"platform/commonUI/edit",
"platform/commonUI/dialog",
"platform/commonUI/formats",
"platform/commonUI/general",
"platform/commonUI/inspect",
"platform/commonUI/mobile",

View File

@ -187,7 +187,7 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
destPath = path.dirname(destination),
streamOptions = {};
if (file.match(/png$/)){
streamOptions.encoding = 'binary';
streamOptions.encoding = null;
} else {
streamOptions.encoding = 'utf8';
}

View File

@ -16,6 +16,23 @@
"implementation": "SinewaveLimitCapability.js"
}
],
"formats": [
{
"key": "example.delta",
"implementation": "SinewaveDeltaFormat.js"
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{ "key": "time", "name": "Time" },
{ "key": "yesterday", "name": "Yesterday" },
{ "key": "delta", "name": "Delta", "format": "example.delta" }
],
"priority": -1
}
],
"types": [
{
"key": "generator",
@ -38,6 +55,11 @@
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [

View File

@ -19,18 +19,8 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
// Styles to temporarily hide non-functional elements
/*global define,Promise*/
/******************************** BROWSE */
.browse-mode {
.browse {
&.top-bar {
display: none;
}
}
.browse-area.holder {
// When .browse.top-bar is hidden, set the top of the browse-area holder
top: $bodyMargin;
}
}
define({
START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day.
});

View File

@ -0,0 +1,68 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
define(
['./SinewaveConstants', 'moment'],
function (SinewaveConstants, moment) {
"use strict";
var START_TIME = SinewaveConstants.START_TIME,
FORMAT_REGEX = /^-?\d+:\d+:\d+$/,
SECOND = 1000,
MINUTE = SECOND * 60,
HOUR = MINUTE * 60;
function SinewaveDeltaFormat() {
}
function twoDigit(v) {
return v >= 10 ? String(v) : ('0' + v);
}
SinewaveDeltaFormat.prototype.format = function (value) {
var delta = Math.abs(value - START_TIME),
negative = value < START_TIME,
seconds = Math.floor(delta / SECOND) % 60,
minutes = Math.floor(delta / MINUTE) % 60,
hours = Math.floor(delta / HOUR);
return (negative ? "-" : "") +
[ hours, minutes, seconds ].map(twoDigit).join(":");
};
SinewaveDeltaFormat.prototype.validate = function (text) {
return FORMAT_REGEX.test(text);
};
SinewaveDeltaFormat.prototype.parse = function (text) {
var negative = text[0] === "-",
parts = text.replace("-", "").split(":");
return [ HOUR, MINUTE, SECOND ].map(function (sz, i) {
return parseInt(parts[i], 10) * sz;
}).reduce(function (a, b) {
return a + b;
}, 0) * (negative ? -1 : 1) + START_TIME;
};
return SinewaveDeltaFormat;
}
);

View File

@ -25,12 +25,12 @@
* Module defining SinewaveTelemetry. Created by vwoeltje on 11/12/14.
*/
define(
[],
function () {
['./SinewaveConstants'],
function (SinewaveConstants) {
"use strict";
var ONE_DAY = 60 * 60 * 24,
firstObservedTime = Math.floor(Date.now() / 1000) - ONE_DAY;
firstObservedTime = Math.floor(SinewaveConstants.START_TIME / 1000);
/**
*
@ -58,6 +58,9 @@ define(
};
generatorData.getDomainValue = function (i, domain) {
// delta uses the same numeric values as the default domain,
// so it's not checked for here, just formatted for display
// differently.
return (i + offset) * 1000 + firstTime * 1000 -
(domain === 'yesterday' ? ONE_DAY : 0);
};

View File

@ -31,10 +31,10 @@
]
},
{
"key": "BrowseTreeController",
"implementation": "BrowseTreeController.js",
"key": "PaneController",
"implementation": "PaneController.js",
"priority": "preferred",
"depends": [ "$scope", "agentService" ]
"depends": [ "$scope", "agentService","$window" ]
},
{
"key": "BrowseObjectController",

View File

@ -41,7 +41,6 @@
</mct-representation>
</div>
</div>
<mct-representation key="representation.selected.key"
mct-object="representation.selected.key && domainObject"
class="abs object-holder">

View File

@ -22,22 +22,27 @@
<div class="abs holder-all browse-mode" ng-controller="BrowseController">
<mct-include key="'topbar-browse'"></mct-include>
<div class="holder browse-area s-browse-area abs browse-wrapper"
ng-controller="BrowseTreeController as tree"
ng-class="tree.visible() ? 'browse-showtree' : 'browse-hidetree'">
<mct-split-pane class='contents abs' anchor='left'>
<div class="abs holder holder-main browse-area s-browse-area browse-wrapper"
ng-controller="PaneController as modelPaneTree"
ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'">
<mct-split-pane class='abs contents'
anchor='left'>
<div class='split-pane-component treeview pane left'>
<div class="holder abs l-mobile">
<!--<a class="mini-tab-icon anchor-left toggle-pane toggle-search mobile-hide"
title="Enable search"
ng-click="modelPaneTree.toggle()"
ng-class="{ collapsed : !modelPaneTree.visible() }"
ng-style="{ opacity: modelPaneTree.visible()? 0:1 }"></a>-->
<div class="holder holder-create-and-search abs l-mobile">
<mct-representation key="'create-button'"
mct-object="navigatedObject"
mct-device="desktop">
</mct-representation>
<div class='holder search-holder abs'
ng-class="{active: treeModel.search}">
<mct-representation key="'search'"
mct-object="domainObject"
<mct-include key="'search'"
ng-model="treeModel">
</mct-representation>
</mct-include>
</div>
<div class='tree-holder abs mobile-tree-holder'
ng-hide="treeModel.search">
@ -50,17 +55,40 @@
</div>
</div>
<mct-splitter class="mobile-hide"></mct-splitter>
<mct-splitter class="splitter-treeview mobile-hide"></mct-splitter>
<div class='split-pane-component items pane right-repr'>
<div class='holder abs l-mobile' id='content-area'>
<mct-representation mct-object="navigatedObject"
key="'browse-object'">
</mct-representation>
<div class='split-pane-component items pane primary-pane right'>
<a class="mini-tab-icon anchor-left toggle-pane toggle-tree"
title="{{ modelPaneTree.visible()? 'Hide' : 'Show' }} this pane"
ng-click="modelPaneTree.toggle()"
ng-class="{ collapsed : !modelPaneTree.visible() }"></a>
<div class='holder holder-object-and-inspector abs l-mobile' id='content-area'
ng-controller="PaneController as modelPaneInspect"
ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'">
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
<div class='split-pane-component t-object pane primary-pane left'>
<mct-representation mct-object="navigatedObject"
key="'browse-object'"
class="abs holder holder-object">
</mct-representation>
</div>
<mct-splitter class="splitter-inspect mobile-hide flush-right edge-shdw"></mct-splitter>
<div class="split-pane-component t-inspect pane right mobile-hide">
<mct-representation key="'object-inspector'"
mct-object="domainObject"
ng-model="treeModel">
</mct-representation>
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect"
title="{{ modelPaneInspect.visible()? 'Hide' : 'Show' }} the Inspection pane"
ng-click="modelPaneInspect.toggle()"
ng-class="{ collapsed : !modelPaneInspect.visible() }"></a>
</div>
</mct-split-pane>
</div>
<div class="key-properties ui-symbol icon mobile-menu-icon desktop-hide"
mct-device="mobile"
ng-click="tree.toggle()">m</div>
</div>
</mct-split-pane>
</div>

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='object-header object-header-mobile'>
<div class='object-header'>
<span class='type-icon ui-symbol'>{{type.getGlyph()}}</span>
<!--span class='type-name mobile-important-hide'>{{type.getName()}}</span-->
<span class="l-elem-wrapper l-flex">

View File

@ -19,13 +19,12 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="menu-element wrapper" ng-controller="ClickAwayController as createController">
<span ng-controller="ClickAwayController as createController">
<div class="s-menu-btn major create-btn" ng-click="createController.toggle()">
<span class="ui-symbol icon type-icon">&#x2b;</span>
<span class="title-label">Create</span>
</div>
<div class="menu super-menu" ng-show="createController.isActive()">
<mct-representation mct-object="domainObject" key="'create-menu'">
</mct-representation>
</div>
</div>
</span>

View File

@ -26,14 +26,8 @@
<div class='ui-symbol profile' title='Shared'>&#x4f;</div>
<mct-representation class="desktop-hide" key="'info-button'" mct-object="domainObject"></mct-representation>
</div>
<div class='item-main abs'>
<div class='ui-symbol icon lg item-type'>
{{type.getGlyph()}}
<span
class="ui-symbol l-icon-link" title="This object is a link"
ng-show="location.isLink()"
></span>
</div>
<div class='item-main abs lg'>
<span class="t-item-icon" ng-class="{ 'l-icon-link':location.isLink() }">{{type.getGlyph()}}</span>
<div class='ui-symbol abs item-open'>}</div>
</div>
<div class='bottom-bar bar abs'>

View File

@ -153,7 +153,6 @@ define(
$scope.$on("$destroy", function () {
navigationService.removeListener(setNavigation);
});
}
return BrowseController;

View File

@ -33,10 +33,12 @@ define(
* @constructor
* @memberof platform/commonUI/browse
*/
function BrowseTreeController($scope, agentService) {
function PaneController($scope, agentService, $window) {
var self = this;
this.agentService = agentService;
this.state = true;
// Fast and cheap: if this has been opened in a new window, hide panes by default
this.state = !$window.opener;
/**
* Callback to invoke when any selection occurs in the tree.
@ -44,7 +46,7 @@ define(
* to the tree representation.
*
* @property {Function} callback
* @memberof platform/commonUI/browse.BrowseTreeController#
* @memberof platform/commonUI/browse.PaneController#
*/
this.callback = function () {
// Note that, since this is a callback to pass, this is not
@ -59,20 +61,20 @@ define(
}
/**
* Toggle the visibility of the tree.
* Toggle the visibility of the pane.
*/
BrowseTreeController.prototype.toggle = function () {
PaneController.prototype.toggle = function () {
this.state = !this.state;
};
/**
* Get the desired visibility state of the tree.
* Get the desired visibility state of the pane.
* @returns {boolean} true when visible
*/
BrowseTreeController.prototype.visible = function () {
PaneController.prototype.visible = function () {
return this.state;
};
return BrowseTreeController;
return PaneController;
}
);

View File

@ -22,22 +22,24 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/BrowseTreeController"],
function (BrowseTreeController) {
["../src/PaneController"],
function (PaneController) {
'use strict';
describe("The BrowseTreeController", function () {
describe("The PaneController", function () {
var mockScope,
mockAgentService,
mockDomainObjects,
mockWindow,
controller;
// We want to reinstantiate for each test case
// because device state can influence constructor-time behavior
function instantiateController() {
return new BrowseTreeController(
return new PaneController(
mockScope,
mockAgentService
mockAgentService,
mockWindow
);
}
@ -58,6 +60,7 @@ define(
"agentService",
[ "isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape" ]
);
mockWindow = jasmine.createSpyObj("$window", ["open"]);
});
it("is initially visible", function () {

View File

@ -1,7 +1,7 @@
[
"BrowseController",
"BrowseObjectController",
"BrowseTreeController",
"PaneController",
"MenuArrowController",
"creation/CreateAction",
"creation/CreateActionProvider",

View File

@ -181,7 +181,7 @@ define(
* @typedef DialogOption
* @property {string} label a label to be displayed as the button
* text for this action
* @property {function} action a function to be called when the
* @property {function} callback a function to be called when the
* button is clicked
*/

View File

@ -0,0 +1,26 @@
{
"name": "Time services bundle",
"description": "Defines interfaces and provides default implementations for handling different time systems.",
"extensions": {
"components": [
{
"provides": "formatService",
"type": "provider",
"implementation": "FormatProvider.js",
"depends": [ "formats[]" ]
}
],
"formats": [
{
"key": "utc",
"implementation": "UTCTimeFormat.js"
}
],
"constants": [
{
"key": "DEFAULT_TIME_FORMAT",
"value": "utc"
}
]
}
}

View File

@ -0,0 +1,114 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
], function (
) {
"use strict";
/**
* An object used to convert between numeric values and text values,
* typically used to display these values to the user and to convert
* user input to a numeric format, particularly for time formats.
* @interface {Format}
*/
/**
* Parse text (typically user input) to a numeric value.
* Behavior is undefined when the text cannot be parsed;
* `validate` should be called first if the text may be invalid.
* @method parse
* @memberof Format#
* @param {string} text the text to parse
* @returns {number} the parsed numeric value
*/
/**
* Determine whether or not some text (typically user input) can
* be parsed to a numeric value by this format.
* @method validate
* @memberof Format#
* @param {string} text the text to parse
* @returns {boolean} true if the text can be parsed
*/
/**
* Convert a numeric value to a text value for display using
* this format.
* @method format
* @memberof Format#
* @param {number} value the numeric value to format
* @returns {string} the text representation of the value
*/
/**
* Provides access to `Format` objects which can be used to
* convert values between human-readable text and numeric
* representations.
* @interface FormatService
*/
/**
* Look up a format by its symbolic identifier.
* @method getFormat
* @memberof FormatService#
* @param {string} key the identifier for this format
* @returns {Format} the format
* @throws {Error} errors when the requested format is unrecognized
*/
/**
* Provides formats from the `formats` extension category.
* @constructor
* @implements {FormatService}
* @memberof platform/commonUI/formats
* @param {Array.<function(new : Format)>} format constructors,
* from the `formats` extension category.
*/
function FormatProvider(formats) {
var formatMap = {};
function addToMap(Format) {
var key = Format.key;
if (key && !formatMap[key]) {
formatMap[key] = new Format();
}
}
formats.forEach(addToMap);
this.formatMap = formatMap;
}
FormatProvider.prototype.getFormat = function (key) {
var format = this.formatMap[key];
if (!format) {
throw new Error("FormatProvider: No format found for " + key);
}
return format;
};
return FormatProvider;
});

View File

@ -19,17 +19,45 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* Classes to be used for lists of properties and values */
/*global define*/
.properties {
.s-row {
border-top: 1px solid $colorInteriorBorder;
font-size: 0.8em;
&:first-child {
border: none;
}
.s-value {
color: #fff;
}
define([
'moment'
], function (
moment
) {
"use strict";
var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
DATE_FORMATS = [
DATE_FORMAT,
"YYYY-MM-DD HH:mm",
"YYYY-MM-DD"
];
/**
* Formatter for UTC timestamps. Interprets numeric values as
* milliseconds since the start of 1970.
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function UTCTimeFormat() {
}
}
UTCTimeFormat.prototype.format = function (value) {
return moment.utc(value).format(DATE_FORMAT);
};
UTCTimeFormat.prototype.parse = function (text) {
return moment.utc(text, DATE_FORMATS).valueOf();
};
UTCTimeFormat.prototype.validate = function (text) {
return moment.utc(text, DATE_FORMATS).isValid();
};
return UTCTimeFormat;
});

View File

@ -0,0 +1,68 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
['../src/FormatProvider'],
function (FormatProvider) {
'use strict';
var KEYS = [ 'a', 'b', 'c' ];
describe("The FormatProvider", function () {
var mockFormats,
mockLog,
mockFormatInstances,
provider;
beforeEach(function () {
mockFormatInstances = KEYS.map(function (k) {
return jasmine.createSpyObj(
'format-' + k,
[ 'parse', 'validate', 'format' ]
);
});
// Return constructors
mockFormats = KEYS.map(function (k, i) {
function MockFormat() { return mockFormatInstances[i]; }
MockFormat.key = k;
return MockFormat;
});
provider = new FormatProvider(mockFormats);
});
it("looks up formats by key", function () {
KEYS.forEach(function (k, i) {
expect(provider.getFormat(k))
.toEqual(mockFormatInstances[i]);
});
});
it("throws an error about unknown formats", function () {
expect(function () {
provider.getFormat('some-unknown-format');
}).toThrow();
});
});
}
);

View File

@ -0,0 +1,56 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
['../src/UTCTimeFormat', 'moment'],
function (UTCTimeFormat, moment) {
'use strict';
describe("The UTCTimeFormat", function () {
var format;
beforeEach(function () {
format = new UTCTimeFormat();
});
it("formats UTC timestamps", function () {
var timestamp = 12345670000,
formatted = format.format(timestamp);
expect(formatted).toEqual(jasmine.any(String));
expect(moment.utc(formatted).valueOf()).toEqual(timestamp);
});
it("validates time inputs", function () {
expect(format.validate("1977-05-25 11:21:22")).toBe(true);
expect(format.validate("garbage text")).toBe(false);
});
it("parses valid input", function () {
var text = "1977-05-25 11:21:22",
parsed = format.parse(text);
expect(parsed).toEqual(jasmine.any(Number));
expect(parsed).toEqual(moment.utc(text).valueOf());
});
});
}
);

View File

@ -0,0 +1,4 @@
[
"FormatProvider",
"UTCTimeFormat"
]

View File

@ -61,13 +61,18 @@
{
"key": "TimeRangeController",
"implementation": "controllers/TimeRangeController.js",
"depends": [ "$scope", "now" ]
"depends": [ "$scope", "formatService", "DEFAULT_TIME_FORMAT", "now" ]
},
{
"key": "DateTimePickerController",
"implementation": "controllers/DateTimePickerController.js",
"depends": [ "$scope", "now" ]
},
{
"key": "DateTimeFieldController",
"implementation": "controllers/DateTimeFieldController.js",
"depends": [ "$scope", "formatService", "DEFAULT_TIME_FORMAT" ]
},
{
"key": "TreeNodeController",
"implementation": "controllers/TreeNodeController.js",
@ -116,6 +121,11 @@
"implementation": "controllers/SelectorController.js",
"depends": [ "objectService", "$scope" ]
},
{
"key": "ObjectInspectorController",
"implementation": "controllers/ObjectInspectorController.js",
"depends": [ "$scope", "objectService" ]
},
{
"key": "BannerController",
"implementation": "controllers/BannerController.js",
@ -245,6 +255,10 @@
"key": "switcher",
"templateUrl": "templates/controls/switcher.html",
"uses": [ "view" ]
},
{
"key": "object-inspector",
"templateUrl": "templates/object-inspector.html"
}
],
"controls": [
@ -255,6 +269,10 @@
{
"key": "datetime-picker",
"templateUrl": "templates/controls/datetime-picker.html"
},
{
"key": "datetime-field",
"templateUrl": "templates/controls/datetime-field.html"
}
],
"licenses": [

View File

@ -1,19 +1,59 @@
{
"metadata": {
"name": "WTD Symbols v2.3",
"lastOpened": 1444267493342,
"created": 1444266013303
"name": "WTD Symbols",
"lastOpened": 1446490786311,
"created": 1446489891263
},
"iconSets": [
{
"selection": [
{
"order": 113,
"id": 92,
"prevSize": 32,
"code": 58901,
"name": "icon-crosshair",
"tempChar": ""
},
{
"order": 110,
"id": 91,
"prevSize": 32,
"code": 58899,
"name": "icon-collapse-pane-left",
"tempChar": ""
},
{
"order": 111,
"id": 90,
"prevSize": 32,
"code": 58900,
"name": "icon-collapse-pane-right",
"tempChar": ""
},
{
"order": 109,
"id": 89,
"prevSize": 32,
"code": 58898,
"name": "icon-save",
"tempChar": ""
},
{
"order": 108,
"id": 88,
"prevSize": 32,
"code": 58897,
"name": "icon-dataset",
"tempChar": ""
},
{
"order": 90,
"id": 87,
"prevSize": 32,
"code": 58896,
"name": "icon-bell",
"tempChar": ""
"tempChar": ""
},
{
"order": 91,
@ -21,7 +61,7 @@
"prevSize": 32,
"code": 58889,
"name": "icon-hourglass",
"tempChar": ""
"tempChar": ""
},
{
"order": 92,
@ -34,7 +74,7 @@
58890
],
"name": "icon-info-v15",
"tempChar": ""
"tempChar": ""
},
{
"order": 93,
@ -42,7 +82,7 @@
"prevSize": 32,
"code": 58887,
"name": "icon-x-in-circle",
"tempChar": ""
"tempChar": ""
},
{
"order": 94,
@ -50,7 +90,7 @@
"prevSize": 32,
"code": 58881,
"name": "icon-datatable",
"tempChar": ""
"tempChar": ""
},
{
"order": 95,
@ -58,7 +98,7 @@
"prevSize": 32,
"code": 58882,
"name": "icon-tabular-scrolling",
"tempChar": ""
"tempChar": ""
},
{
"order": 96,
@ -66,7 +106,7 @@
"prevSize": 32,
"code": 58884,
"name": "icon-tabular",
"tempChar": ""
"tempChar": ""
},
{
"order": 97,
@ -74,7 +114,7 @@
"prevSize": 32,
"code": 58885,
"name": "icon-calendar",
"tempChar": ""
"tempChar": ""
},
{
"order": 98,
@ -82,7 +122,7 @@
"prevSize": 32,
"code": 58886,
"name": "icon-paint-bucket",
"tempChar": ""
"tempChar": ""
},
{
"order": 99,
@ -90,7 +130,7 @@
"prevSize": 32,
"code": 123,
"name": "icon-pointer-left",
"tempChar": ""
"tempChar": ""
},
{
"order": 100,
@ -98,7 +138,7 @@
"prevSize": 32,
"code": 125,
"name": "icon-pointer-right",
"tempChar": ""
"tempChar": ""
},
{
"order": 101,
@ -106,7 +146,7 @@
"prevSize": 32,
"code": 80,
"name": "icon-person",
"tempChar": ""
"tempChar": ""
},
{
"order": 102,
@ -114,7 +154,7 @@
"prevSize": 32,
"code": 232,
"name": "icon-chain-links",
"tempChar": ""
"tempChar": ""
},
{
"order": 103,
@ -122,7 +162,7 @@
"prevSize": 32,
"code": 115,
"name": "icon-database-in-brackets",
"tempChar": ""
"tempChar": ""
},
{
"order": 104,
@ -130,7 +170,7 @@
"prevSize": 32,
"code": 114,
"name": "icon-refresh",
"tempChar": ""
"tempChar": ""
},
{
"order": 105,
@ -138,7 +178,7 @@
"prevSize": 32,
"code": 108,
"name": "icon-lock",
"tempChar": ""
"tempChar": ""
},
{
"order": 106,
@ -146,7 +186,7 @@
"prevSize": 32,
"code": 51,
"name": "icon-box-with-dashed-lines",
"tempChar": ""
"tempChar": ""
},
{
"order": 10,
@ -154,7 +194,7 @@
"prevSize": 32,
"code": 58880,
"name": "icon-box-with-arrow-cursor",
"tempChar": ""
"tempChar": ""
},
{
"order": 11,
@ -162,7 +202,7 @@
"prevSize": 32,
"code": 65,
"name": "icon-activity-mode",
"tempChar": ""
"tempChar": ""
},
{
"order": 12,
@ -170,7 +210,7 @@
"prevSize": 32,
"code": 97,
"name": "icon-activity",
"tempChar": ""
"tempChar": ""
},
{
"order": 87,
@ -178,7 +218,7 @@
"prevSize": 32,
"code": 33,
"name": "icon-alert-rect",
"tempChar": ""
"tempChar": ""
},
{
"order": 14,
@ -186,7 +226,7 @@
"prevSize": 32,
"code": 58883,
"name": "icon-alert-triangle",
"tempChar": ""
"tempChar": ""
},
{
"order": 15,
@ -194,7 +234,7 @@
"prevSize": 32,
"code": 238,
"name": "icon-arrow-double-down",
"tempChar": ""
"tempChar": ""
},
{
"order": 16,
@ -202,7 +242,7 @@
"prevSize": 32,
"code": 235,
"name": "icon-arrow-double-up",
"tempChar": ""
"tempChar": ""
},
{
"order": 2,
@ -210,7 +250,7 @@
"prevSize": 32,
"code": 118,
"name": "icon-arrow-down",
"tempChar": ""
"tempChar": ""
},
{
"order": 19,
@ -218,7 +258,7 @@
"prevSize": 32,
"code": 60,
"name": "icon-arrow-left",
"tempChar": ""
"tempChar": ""
},
{
"order": 20,
@ -226,7 +266,7 @@
"prevSize": 32,
"code": 62,
"name": "icon-arrow-right",
"tempChar": ""
"tempChar": ""
},
{
"order": 21,
@ -234,7 +274,7 @@
"prevSize": 32,
"code": 236,
"name": "icon-arrow-tall-down",
"tempChar": ""
"tempChar": ""
},
{
"order": 22,
@ -242,7 +282,7 @@
"prevSize": 32,
"code": 237,
"name": "icon-arrow-tall-up",
"tempChar": ""
"tempChar": ""
},
{
"order": 23,
@ -250,7 +290,7 @@
"prevSize": 32,
"code": 94,
"name": "icon-arrow-up",
"tempChar": ""
"tempChar": ""
},
{
"order": 24,
@ -258,7 +298,7 @@
"prevSize": 32,
"code": 73,
"name": "icon-arrows-out",
"tempChar": ""
"tempChar": ""
},
{
"order": 25,
@ -266,7 +306,7 @@
"prevSize": 32,
"code": 58893,
"name": "icon-arrows-right-left",
"tempChar": ""
"tempChar": ""
},
{
"order": 33,
@ -274,7 +314,7 @@
"prevSize": 32,
"code": 53,
"name": "icon-arrows-up-down",
"tempChar": ""
"tempChar": ""
},
{
"order": 26,
@ -282,7 +322,7 @@
"prevSize": 32,
"code": 42,
"name": "icon-asterisk",
"tempChar": ""
"tempChar": ""
},
{
"order": 27,
@ -290,7 +330,7 @@
"prevSize": 32,
"code": 72,
"name": "icon-autoflow-tabular",
"tempChar": ""
"tempChar": ""
},
{
"order": 28,
@ -298,7 +338,7 @@
"prevSize": 32,
"code": 224,
"name": "icon-box",
"tempChar": ""
"tempChar": ""
},
{
"order": 29,
@ -306,7 +346,7 @@
"prevSize": 32,
"code": 50,
"name": "icon-check",
"tempChar": ""
"tempChar": ""
},
{
"order": 30,
@ -314,7 +354,7 @@
"prevSize": 32,
"code": 67,
"name": "icon-clock",
"tempChar": ""
"tempChar": ""
},
{
"order": 31,
@ -322,7 +362,7 @@
"prevSize": 32,
"code": 46,
"name": "icon-connectivity",
"tempChar": ""
"tempChar": ""
},
{
"order": 32,
@ -330,7 +370,7 @@
"prevSize": 32,
"code": 100,
"name": "icon-database-query",
"tempChar": ""
"tempChar": ""
},
{
"order": 17,
@ -338,7 +378,7 @@
"prevSize": 32,
"code": 68,
"name": "icon-database",
"tempChar": ""
"tempChar": ""
},
{
"order": 35,
@ -346,7 +386,7 @@
"prevSize": 32,
"code": 81,
"name": "icon-dictionary",
"tempChar": ""
"tempChar": ""
},
{
"order": 36,
@ -354,7 +394,7 @@
"prevSize": 32,
"code": 242,
"name": "icon-duplicate",
"tempChar": ""
"tempChar": ""
},
{
"order": 37,
@ -362,7 +402,7 @@
"prevSize": 32,
"code": 102,
"name": "icon-folder-new",
"tempChar": ""
"tempChar": ""
},
{
"order": 38,
@ -370,7 +410,7 @@
"prevSize": 32,
"code": 70,
"name": "icon-folder",
"tempChar": ""
"tempChar": ""
},
{
"order": 39,
@ -378,7 +418,7 @@
"prevSize": 32,
"code": 95,
"name": "icon-fullscreen-collapse",
"tempChar": ""
"tempChar": ""
},
{
"order": 40,
@ -386,7 +426,7 @@
"prevSize": 32,
"code": 122,
"name": "icon-fullscreen-expand",
"tempChar": ""
"tempChar": ""
},
{
"order": 41,
@ -394,7 +434,7 @@
"prevSize": 32,
"code": 71,
"name": "icon-gear",
"tempChar": ""
"tempChar": ""
},
{
"order": 49,
@ -402,7 +442,7 @@
"prevSize": 32,
"code": 227,
"name": "icon-image",
"tempChar": ""
"tempChar": ""
},
{
"order": 42,
@ -410,7 +450,7 @@
"prevSize": 32,
"code": 225,
"name": "icon-layers",
"tempChar": ""
"tempChar": ""
},
{
"order": 43,
@ -418,7 +458,7 @@
"prevSize": 32,
"code": 76,
"name": "icon-layout",
"tempChar": ""
"tempChar": ""
},
{
"order": 44,
@ -426,7 +466,7 @@
"prevSize": 32,
"code": 226,
"name": "icon-line-horz",
"tempChar": ""
"tempChar": ""
},
{
"order": 75,
@ -434,7 +474,7 @@
"prevSize": 32,
"code": 244,
"name": "icon-link",
"tempChar": ""
"tempChar": ""
},
{
"order": 46,
@ -442,7 +482,7 @@
"prevSize": 32,
"code": 88,
"name": "icon-magnify-in",
"tempChar": ""
"tempChar": ""
},
{
"order": 47,
@ -450,7 +490,7 @@
"prevSize": 32,
"code": 89,
"name": "icon-magnify-out",
"tempChar": ""
"tempChar": ""
},
{
"order": 48,
@ -458,7 +498,7 @@
"prevSize": 32,
"code": 77,
"name": "icon-magnify",
"tempChar": ""
"tempChar": ""
},
{
"order": 34,
@ -466,7 +506,7 @@
"prevSize": 32,
"code": 109,
"name": "icon-menu",
"tempChar": ""
"tempChar": ""
},
{
"order": 50,
@ -474,7 +514,7 @@
"prevSize": 32,
"code": 243,
"name": "icon-move",
"tempChar": ""
"tempChar": ""
},
{
"order": 51,
@ -482,7 +522,7 @@
"prevSize": 32,
"code": 121,
"name": "icon-new-window",
"tempChar": ""
"tempChar": ""
},
{
"order": 52,
@ -490,7 +530,7 @@
"prevSize": 32,
"code": 111,
"name": "icon-object",
"tempChar": ""
"tempChar": ""
},
{
"order": 73,
@ -498,7 +538,7 @@
"prevSize": 32,
"code": 63,
"name": "icon-object-unknown",
"tempChar": ""
"tempChar": ""
},
{
"order": 53,
@ -506,7 +546,7 @@
"prevSize": 32,
"code": 86,
"name": "icon-packet",
"tempChar": ""
"tempChar": ""
},
{
"order": 54,
@ -514,7 +554,7 @@
"prevSize": 32,
"code": 234,
"name": "icon-page",
"tempChar": ""
"tempChar": ""
},
{
"order": 55,
@ -522,7 +562,7 @@
"prevSize": 32,
"code": 241,
"name": "icon-pause",
"tempChar": ""
"tempChar": ""
},
{
"order": 56,
@ -530,7 +570,7 @@
"prevSize": 32,
"code": 112,
"name": "icon-pencil",
"tempChar": ""
"tempChar": ""
},
{
"order": 65,
@ -538,7 +578,7 @@
"prevSize": 32,
"code": 79,
"name": "icon-people",
"tempChar": ""
"tempChar": ""
},
{
"order": 57,
@ -546,7 +586,7 @@
"prevSize": 32,
"code": 239,
"name": "icon-play",
"tempChar": ""
"tempChar": ""
},
{
"order": 58,
@ -554,7 +594,7 @@
"prevSize": 32,
"code": 233,
"name": "icon-plot-resource",
"tempChar": ""
"tempChar": ""
},
{
"order": 59,
@ -562,7 +602,7 @@
"prevSize": 32,
"code": 43,
"name": "icon-plus",
"tempChar": ""
"tempChar": ""
},
{
"order": 60,
@ -570,7 +610,7 @@
"prevSize": 32,
"code": 45,
"name": "icon-minus",
"tempChar": ""
"tempChar": ""
},
{
"order": 61,
@ -578,7 +618,7 @@
"prevSize": 32,
"code": 54,
"name": "icon-sine",
"tempChar": ""
"tempChar": ""
},
{
"order": 62,
@ -586,7 +626,7 @@
"prevSize": 32,
"code": 228,
"name": "icon-T",
"tempChar": ""
"tempChar": ""
},
{
"order": 63,
@ -594,7 +634,7 @@
"prevSize": 32,
"code": 116,
"name": "icon-telemetry-panel",
"tempChar": ""
"tempChar": ""
},
{
"order": 64,
@ -602,7 +642,7 @@
"prevSize": 32,
"code": 84,
"name": "icon-telemetry",
"tempChar": ""
"tempChar": ""
},
{
"order": 18,
@ -610,7 +650,7 @@
"prevSize": 32,
"code": 246,
"name": "icon-thumbs-strip",
"tempChar": ""
"tempChar": ""
},
{
"order": 67,
@ -618,7 +658,7 @@
"prevSize": 32,
"code": 83,
"name": "icon-timeline",
"tempChar": ""
"tempChar": ""
},
{
"order": 68,
@ -626,7 +666,7 @@
"prevSize": 32,
"code": 245,
"name": "icon-timer",
"tempChar": ""
"tempChar": ""
},
{
"order": 69,
@ -634,7 +674,7 @@
"prevSize": 32,
"code": 90,
"name": "icon-trash",
"tempChar": ""
"tempChar": ""
},
{
"order": 70,
@ -642,7 +682,7 @@
"prevSize": 32,
"code": 229,
"name": "icon-two-parts-both",
"tempChar": ""
"tempChar": ""
},
{
"order": 71,
@ -650,7 +690,7 @@
"prevSize": 32,
"code": 231,
"name": "icon-two-parts-one-only",
"tempChar": ""
"tempChar": ""
},
{
"order": 72,
@ -658,7 +698,7 @@
"prevSize": 32,
"code": 120,
"name": "icon-x-heavy",
"tempChar": ""
"tempChar": ""
},
{
"order": 66,
@ -666,7 +706,7 @@
"prevSize": 32,
"code": 58946,
"name": "icon-x",
"tempChar": ""
"tempChar": ""
}
],
"id": 2,
@ -681,6 +721,121 @@
"height": 1024,
"prevSize": 32,
"icons": [
{
"id": 92,
"paths": [
"M514 2c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM860.2 450h-282.2v-282.2c69.6 12.8 133.8 46.2 185 97.4 51 51 84.4 115.2 97.2 184.8zM450 167.8v282.2h-282.2c12.8-69.6 46.2-133.8 97.4-185 51-51 115.2-84.4 184.8-97.2zM167.8 578h282.2v282.2c-69.6-12.8-133.8-46.2-185-97.4-51-51-84.4-115.2-97.2-184.8zM578 860.2v-282.2h282.2c-12.8 69.6-46.2 133.8-97.4 185-51 51-115.2 84.4-184.8 97.2z"
],
"attrs": [
{
"fill": "rgb(0, 0, 0)"
}
],
"isMulticolor": false,
"grid": 0,
"tags": [
"icon-crosshair"
],
"colorPermutations": {
"125525525516161751": [
0
]
}
},
{
"id": 91,
"paths": [
"M256 0h-256v1024h256c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192z",
"M512 320l512 320v-640z"
],
"attrs": [
{
"fill": "rgb(0, 0, 0)"
},
{
"fill": "rgb(0, 0, 0)"
}
],
"isMulticolor": false,
"grid": 0,
"tags": [
"icon-collapse-pane-left"
],
"colorPermutations": {
"125525525516161751": [
0,
0
]
}
},
{
"id": 90,
"paths": [
"M768 0h256v1024h-256c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192z",
"M512 320l-512 320v-640z"
],
"attrs": [
{
"fill": "rgb(0, 0, 0)"
},
{
"fill": "rgb(0, 0, 0)"
}
],
"isMulticolor": false,
"grid": 0,
"tags": [
"icon-collapse-pane-right"
],
"colorPermutations": {
"125525525516161751": [
0,
0
]
}
},
{
"id": 89,
"paths": [
"M192.2 576c-0.2 0-0.2 0 0 0l-0.2 448h640v-447.8c0 0 0 0-0.2-0.2h-639.6z",
"M978.8 210.8l-165.4-165.4c-25-25-74.2-45.4-109.4-45.4h-576c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128v-448c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v448c70.4 0 128-57.6 128-128v-576c0-35.2-20.4-84.4-45.2-109.2zM704 256c0 35.2-28.8 64-64 64h-448c-35.2 0-64-28.8-64-64v-192h320v192h128v-192h128v192z"
],
"attrs": [
{
"fill": "rgb(0, 0, 0)"
},
{
"fill": "rgb(0, 0, 0)"
}
],
"isMulticolor": false,
"grid": 0,
"tags": [
"icon-save-v2"
],
"colorPermutations": {
"125525525516161751": [
0,
0
]
}
},
{
"id": 88,
"paths": [
"M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z",
"M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128zM320 896h-128v-320h128v320zM576 896h-128v-320h128v320zM832 896h-128v-320h128v320z"
],
"attrs": [],
"isMulticolor": false,
"grid": 0,
"tags": [
"icon-dataset"
],
"colorPermutations": {
"125525525516161751": []
}
},
{
"id": 87,
"paths": [

View File

@ -88,5 +88,10 @@
<glyph unicode="&#xe609;" glyph-name="icon-hourglass" d="M1024 960h-1024c0-282.8 229.2-512 512-512s512 229.2 512 512zM512 576c-102.6 0-199 40-271.6 112.4-41.2 41.2-72 90.2-90.8 143.6h724.6c-18.8-53.4-49.6-102.4-90.8-143.6-72.4-72.4-168.8-112.4-271.4-112.4zM512 448c-282.8 0-512-229.2-512-512h1024c0 282.8-229.2 512-512 512z" />
<glyph unicode="&#xe60d;" glyph-name="icon-arrows-right-left" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
<glyph unicode="&#xe610;" glyph-name="icon-bell" d="M512-64c106 0 192 86 192 192h-384c0-106 86-192 192-192zM896 512v64c0 212-172 384-384 384s-384-172-384-384v-64c0-70.6-57.4-128-128-128v-128h1024v128c-70.6 0-128 57.4-128 128z" />
<glyph unicode="&#xe611;" glyph-name="icon-dataset" d="M896 768h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 512h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128zM320 64h-128v320h128v-320zM576 64h-128v320h128v-320zM832 64h-128v320h128v-320z" />
<glyph unicode="&#xe612;" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
<glyph unicode="&#xe613;" glyph-name="icon-collapse-pane-left" d="M256 960h-256v-1024h256c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM512 640l512-320v640z" />
<glyph unicode="&#xe614;" glyph-name="icon-collapse-pane-right" d="M768 960h256v-1024h-256c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192zM512 640l-512-320v640z" />
<glyph unicode="&#xe615;" glyph-name="icon-crosshair" d="M514 958c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM860.2 510h-282.2v282.2c69.6-12.8 133.8-46.2 185-97.4 51-51 84.4-115.2 97.2-184.8zM450 792.2v-282.2h-282.2c12.8 69.6 46.2 133.8 97.4 185 51 51 115.2 84.4 184.8 97.2zM167.8 382h282.2v-282.2c-69.6 12.8-133.8 46.2-185 97.4-51 51-84.4 115.2-97.2 184.8zM578 99.8v282.2h282.2c-12.8-69.6-46.2-133.8-97.4-185-51-51-115.2-84.4-184.8-97.2z" />
<glyph unicode="&#xe642;" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -42,7 +42,11 @@ $ueFooterH: 25px;
$ueColMargin: 1.5%;
$ueAppLogoW: 105px;
$ueEditToolBarH: 25px;
$ueBrowseLeftPaneW: 25%;
$ueBrowseLeftPaneTreeW: 25%;
$ueBrowseRightPaneInspectW: 20%;
$ueCollapsedPaneEdgeM: 20px;
$uePaneMiniTabH: $ueTopBarH;
$uePaneMiniTabW: 9px;
$ueEditLeftPaneW: 75%;
$treeSearchInputBarH: 25px;
$ueTimeControlH: (33px, 20px, 20px);
@ -57,7 +61,8 @@ $ueBrowseGridItemBottomBarH: 30px;
$itemPadLR: 5px;
// Tree
$treeVCW: 10px;
$treeTypeIconH: 16px;
$treeTypeIconH: 1.4em; // was 16px
$treeTypeIconHPx: 16px;
$treeTypeIconW: 20px;
$treeContextTriggerW: 20px;
// Tabular

View File

@ -34,10 +34,6 @@
font-style: normal;
}
.ui-symbol {
font-family: 'symbolsfont';
}
/************************** HTML ENTITIES */
a {
color: $colorA;
@ -55,7 +51,7 @@ body, html {
color: $colorBodyFg;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 100%;
//font-weight: 500;
font-weight: 200;
height: 100%;
width: 100%;
overflow: hidden;

View File

@ -29,11 +29,14 @@
}
.ui-symbol {
font-family: 'symbolsfont';
&.type-icon {
color: $colorObjHdrIc;
}
&.icon {
color: $colorKey;
//position: relative;
font-size: inherit;
&.alert {
color: $colorAlert;
&:hover {
@ -69,18 +72,32 @@
position: absolute;
}
//.tree-item .type-icon {
// font-size: 16px; // 16px is crisp size
//}
.l-icon-link:before {
content: "\f4";
}
.l-icon-alert {
display: none !important; // Remove this when alerts are enabled
&:before {
color: $colorAlert;
content: "!";
}
}
// NEW!!
.t-item-icon {
// Used in grid-item.html, tree-item, inspector location, more?
@extend .ui-symbol;
@extend .icon;
display: inline-block;
line-height: normal; // This is Ok for the symbolsfont
position: relative;
&.l-icon-link {
&:before {
color: $colorIconLink;
content: "\f4";
height: auto; width: auto;
position: absolute;
left: 0; top: 0; right: 0; bottom: 10%;
@include transform-origin(bottom, left);
@include transform(scale(0.3));
z-index: 2;
}
}
}

View File

@ -0,0 +1,107 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* Styles for the Inspector pane */
.l-inspect,
.l-inspect table tr td {
font-size: 0.7rem;
}
.l-inspect {
@extend .abs;
background: $colorInspectorBg;
color: $colorInspectorFg;
line-height: 140%;
.pane-header {
color: pushBack($colorInspectorFg, 20%);
font-size: 0.8rem;
&:before {
color: pushBack($colorInspectorFg, 10%);
content:'\e615'; // e615 Crosshair symbol
display: inline;
font-family: symbolsfont;
margin-right: $interiorMargin;
vertical-align: bottom;
}
}
ul li,
em {
display: block;
position: relative;
}
ul li {
margin-bottom: $interiorMarginLg;
}
em {
@include border-radius($basicCr);
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
margin-bottom: $interiorMargin;
padding: $formTBPad $formLRPad;
text-transform: uppercase;
}
.inspector-properties {
&:not(.first) {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
padding: $interiorMarginSm 0;
.label {
color: $colorInspectorPropName;
text-transform: uppercase;
}
.value {
color: $colorInspectorPropVal;
word-break: break-all;
}
}
.inspector-location {
//line-height: 180%;
.location-item {
cursor: pointer;
display: inline-block;
position: relative;
padding: 2px 4px;
&:hover {
background: $colorItemTreeHoverBg;
color: $colorItemTreeHoverFg;
.icon {
color: $colorItemTreeIconHover;
}
}
}
&:not(.last) .t-object-label .t-title-label:after {
color: pushBack($colorInspectorFg, 15%);
content: '\3e';
display: inline-block;
font-family: symbolsfont;
font-size: 8px;
line-height: inherit;
margin-left: $interiorMarginSm;
width: 4px;
}
}
}

View File

@ -29,7 +29,7 @@
@import "helpers/bubbles";
@import "helpers/splitter";
@import "helpers/wait-spinner";
@import "properties";
@import "inspector";
/********************************* CONTROLS */
@import "controls/breadcrumb";
@ -69,7 +69,6 @@
@import "lists/tabular";
@import "plots/plots-main";
@import "iframe";
@import "hide-non-functional";
@import "views";
@import "items/item";
@import "mobile/item";

View File

@ -41,36 +41,41 @@
width: $d;
}
@mixin trans-prop-nice($props, $t: 500ms) {
@if $t == 0 {
@mixin trans-prop-nice($props, $dur: 500ms, $delay: 0) {
// Multiple $props must be in parans like this: (left, right)
@if $dur == 0 {
@include transition-property(none);
} @else {
@include transition-property($props);
@include transition-duration($t);
@include transition-duration($dur);
@include transition-timing-function(ease-in-out);
@include transition-delay($delay);
}
}
@mixin trans-prop-nice-fade($t: 0.5s) {
@if $t == 0 {
@mixin trans-prop-nice-fade($dur: 500ms, $delay: 0) {
@if $dur == 0 {
@include transition-property(none);
} @else {
@include transition-property(visibility, opacity, background-color, border-color);
@include transition-duration($t);
@include transition-property(opacity, background-color, border-color, color);
@include transition-duration($dur);
@include transition-timing-function(ease-in-out);
@include transition-delay($delay);
}
}
@mixin trans-prop-nice-resize-h($t: 0.5s) {
@mixin trans-prop-nice-resize-h($dur: 500ms, $delay: 0) {
@include transition-property(height, bottom, top);
@include transition-duration($t);
@include transition-duration($dur);
@include transition-timing-function(ease-in-out);
@include transition-delay($delay);
}
@mixin trans-prop-nice-resize-w($t: 0.5s) {
@mixin trans-prop-nice-resize-w($dur: 500ms, $delay: 0) {
@include transition-property(width, left, right);
@include transition-duration($t);
@include transition-duration($dur);
@include transition-timing-function(ease-in-out);
@include transition-delay($delay);
}
@mixin triangle-right($size, $color) {
@ -173,31 +178,28 @@
}
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
&:before {
@include trans-prop-nice("border-color", 0.75s);
content: '';
display: block;
height: auto;
pointer-events: none;
position: absolute;
z-index: 2;
//&:before {
//@include trans-prop-nice("border-color", 25ms);
content: '';
display: block;
//height: auto;
pointer-events: none;
position: absolute;
z-index: 2;
@if $direction == "horizontal" {
border-top: $w $style darken($b, 15%);
top: 2px;
left: 5px;
right: 5px;
@if $direction == "horizontal" {
border-top: $w $style darken($b, 15%);
top: 2px;
left: 5px;
right: 5px;
height: 1px;
} @else if $direction == "vertical" {
border-left: $w $style darken($b, 15%);
left: 2px;
bottom: 5px;
top: 5px;
}
}
&:not(.disabled):hover:before {
@include trans-prop-nice("border-color", 25ms);
border-color: $colorGrippyInteriorHover;
} @else if $direction == "vertical" {
border-left: $w $style darken($b, 15%);
left: 2px;
bottom: 5px;
top: 5px;
width: 1px;
}
}
@ -273,6 +275,12 @@
@return percentage($d);
}
@function splitterHandleInset($splitterD: 21px, $splitterHandleD: 1px) {
// Space to either side of the handle
@return ($splitterD - $splitterHandleD) * 0.5;
}
/*********************************************** CONTROLS, FORM ELEMENTS */
@mixin containerBase($bg: $colorBodyBg, $fg: $colorBodyFg) {
@ -364,10 +372,7 @@
/* This doesn't work on an element inside an element with absolute positioning that has height: auto */
//position: relative;
top: 50%;
@include webkitProp(transform, translateY(-50%));
//-webkit-transform: translateY(-50%);
//-ms-transform: translateY(-50%);
//transform: translateY(-50%);
@include transform(translateY(-50%));
}
@mixin verticalCenterBlock($holderH, $itemH) {

View File

@ -22,13 +22,17 @@
$baseRatio: 1.5;
$pad: $interiorMargin * $baseRatio;
.s-btn {
@include box-sizing(border-box);
.s-btn,
.s-icon-btn {
@include user-select(none);
cursor: pointer;
text-decoration: none;
height: $btnStdH;
line-height: $btnStdH;
}
.s-btn {
@include box-sizing(border-box);
padding: 0 $pad;
font-size: 0.7rem;
@ -89,6 +93,155 @@ $pad: $interiorMargin * $baseRatio;
}
}
.s-icon-btn {
@extend .ui-symbol;
color: $colorBtnIcon;
&:hover {
color: lighten($colorBtnIcon, $ltGamma);
}
}
.mini-tab {
// Meant to be used as pane hide/show control elements in concert with mct-splitter
//@extend .ui-symbol;
@include desktop {
//@include test(green);
$iconH: $uePaneMiniTabH;
$iconW: $uePaneMiniTabW;
$iconInnerLR: 0;
$arwD: 9px;
$arwOffsetX: 0px;
$arwAnimOffsetX: 2px + $iconInnerLR;
$cBg: pullForward($colorBodyBg, 15%);
$cFg: $cBg;
@include border-radius($basicCr);
//@include boxShdw($shdwBtns);
@include box-sizing(border-box);
@include trans-prop-nice((color, background-color), 100ms);
color: $cFg;
cursor: pointer;
font-family: symbolsfont;
font-size: $arwD;
display: block;
position: absolute;
line-height: $iconH;
height: $iconH; width: $iconW;
text-align: center;
&:hover {
//background-color: $cBg;
color: $colorKey; //pullForward($cFg, $ltGamma);
}
&.collapsed {
// State when the pane this element controls has been collapsed
@include btnSubtle($colorBtnBg, $colorKey, $colorBtnFg, $colorBtnIcon);
&:before { opacity: 0; }
&:after { opacity: 1; }
&:hover {
&:before { opacity: 1; }
&:after { opacity: 0; }
}
}
&:before,
&:after {
//@include test();
@include trans-prop-nice((left, right, opacity), 250ms);
display: block;
height: 100%;
position: absolute;
}
&:before {
// Always the arrow icon
//@include test(green);
//font-size: $arwD;
width: $arwD;
}
&:after {
// Always icon; content is set in _layout.scss
width: 100%;
text-align: center;
opacity: 0;
}
&.anchor-left {
// |<
text-align: right;
&:before {
content:'\3c'; // Collapse left icon e613
right: $iconInnerLR;
}
//&:hover:before { right: $arwAnimOffsetX; }
&.collapsed {
@include border-left-radius(0);
text-align: left;
&:before {
content:'\3e';
left: $iconInnerLR;
}
&:hover:before { left: $arwAnimOffsetX; }
}
}
&.anchor-right {
// >|
text-align: left;
&:before {
content:'\3e'; // Collapse right icon e614
left: $iconInnerLR;
}
//&:hover:before { left: $arwAnimOffsetX; }
&.collapsed {
@include border-right-radius(0);
&:before {
text-align: right;
content:'\3c';
right: $iconInnerLR;
}
&:hover:before { right: $arwAnimOffsetX; }
}
}
}
}
.mini-tab-icon {
// Meant to be used as pane hide/show control elements in concert with mct-splitter
//@extend .ui-symbol;
@include desktop {
$d: $uePaneMiniTabW;
//@include trans-prop-nice(transform, 150ms);
color: pullForward($colorBodyBg, 15%);
cursor: pointer;
display: block;
font-family: symbolsfont;
font-size: $d;
position: absolute;
height: $d; width: $d;
line-height: $d;
overflow: hidden;
word-break: break-all;
&:before,
&:after {
position: absolute;
display: inherit;
}
&:before {
content: '\78'; // X icon
}
&:hover {
color: $colorKey;
//@include transform(scale(1.2));
}
}
}
.l-btn-set {
// Buttons that have a very tight conceptual grouping - no internal space between them.
// Structure: .btn-set > mct-representation class=first|last > .s-btn

View File

@ -21,246 +21,239 @@
*****************************************************************************/
.accordion {
$accordionHeadH: 18px;
margin-top: $interiorMargin;
&:first-child {
margin-top: 0;
}
.accordion-head {
$op: 0.2;
@include border-radius($basicCr * 0.75);
@include box-sizing("border-box");
background: rgba($colorBodyFg, $op);
cursor: pointer;
font-size: 0.75em;
line-height: $accordionHeadH;
margin-bottom: $interiorMargin;
padding: 0 $interiorMargin;
position: absolute;
top: 0;
right: 0;
bottom: auto;
left: 0;
width: auto;
height: $accordionHeadH;
text-transform: uppercase;
&:hover {
background: rgba($colorBodyFg, $op * 2);
}
&:after {
content: "^";
display: block;
font-family: 'symbolsfont';
font-size: 0.9em;
position: absolute;
right: $interiorMargin;
text-transform: none;
top: 0;
}
&:not(.expanded):after {
content: "v";
}
}
.accordion-contents {
position: absolute;
top: $accordionHeadH + $interiorMargin;
right: 0;
bottom: 0;
left: 0;
overflow-y: auto;
overflow-x: hidden;
}
$accordionHeadH: 18px;
margin-top: $interiorMargin;
&:first-child {
margin-top: 0;
}
.accordion-head {
$op: 0.2;
@include border-radius($basicCr * 0.75);
@include box-sizing("border-box");
background: rgba($colorBodyFg, $op);
cursor: pointer;
font-size: 0.75em;
line-height: $accordionHeadH;
margin-bottom: $interiorMargin;
padding: 0 $interiorMargin;
position: absolute;
top: 0;
right: 0;
bottom: auto;
left: 0;
width: auto;
height: $accordionHeadH;
text-transform: uppercase;
&:hover {
background: rgba($colorBodyFg, $op * 2);
}
&:after {
content: "^";
display: block;
font-family: 'symbolsfont';
font-size: 0.9em;
position: absolute;
right: $interiorMargin;
text-transform: none;
top: 0;
}
&:not(.expanded):after {
content: "v";
}
}
.accordion-contents {
position: absolute;
top: $accordionHeadH + $interiorMargin;
right: 0;
bottom: 0;
left: 0;
overflow-y: auto;
overflow-x: hidden;
}
}
.l-composite-control {
vertical-align: middle;
&.l-checkbox {
.composite-control-label {
line-height: 18px;
}
}
vertical-align: middle;
&.l-checkbox {
.composite-control-label {
line-height: 18px;
}
}
}
.l-control-group {
// Buttons that have a conceptual grouping - internal space between, and a divider between groups.
// @include test();
@include box-sizing(border-box);
border-left: 1px solid $colorInteriorBorder;
display: inline-block;
padding: 0 $interiorMargin;
position: relative;
&:first-child {
border-left: none;
padding-left: 0;
}
// Buttons that have a conceptual grouping - internal space between, and a divider between groups.
// @include test();
@include box-sizing(border-box);
border-left: 1px solid $colorInteriorBorder;
display: inline-block;
padding: 0 $interiorMargin;
position: relative;
&:first-child {
border-left: none;
padding-left: 0;
}
}
.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;
z-index: 5;
// Control shown when hovering over an object, like plots and imagery
// Default position is upper right
$p: $interiorMargin;
position: absolute;
top: $p;
right: $p;
z-index: 5;
}
.s-local-controls {
font-size: 0.7rem;
font-size: 0.7rem;
}
label.checkbox.custom {
$bg: pullForward($colorBodyBg, 10%);
$d: $formRowCtrlsH;
cursor: pointer;
display: inline-block;
line-height: $d;
margin-right: $interiorMargin * 4;
padding-left: $d + $interiorMargin;
position: relative;
vertical-align: middle; // was top
em {
color: $colorBodyFg;
display: inline-block;
height: $d;
min-width: $d;
&:before {
@include border-radius($basicCr * .75);
background: $bg;
//border-bottom: 1px solid lighten($bg, 10%);
@include box-shadow(inset rgba(black, 0.4) 0 1px 2px);
box-sizing: border-box;
content: " ";
font-family: 'symbolsfont';
font-size: 0.8em;
display: inline-block;
margin-right: $interiorMargin;
height: $d;
width: $d;
left: 0;
top: 0;
position: absolute;
text-align: center;
}
}
&.no-text {
overflow: hidden;
margin-right: 0;
padding-left: 0;
height: $d;
width: $d;
em {
overflow: hidden;
}
}
input {
display: none;
&:checked ~ em:before {
background: $colorCheck;
color: lighten($colorCheck, 50%);
content: "2";
}
}
$bg: pullForward($colorBodyBg, 10%);
$d: $formRowCtrlsH;
cursor: pointer;
display: inline-block;
line-height: $d;
margin-right: $interiorMargin * 4;
padding-left: $d + $interiorMargin;
position: relative;
vertical-align: middle; // was top
em {
color: $colorBodyFg;
display: inline-block;
height: $d;
min-width: $d;
&:before {
@include border-radius($basicCr * .75);
background: $bg;
//border-bottom: 1px solid lighten($bg, 10%);
@include box-shadow(inset rgba(black, 0.4) 0 1px 2px);
box-sizing: border-box;
content: " ";
font-family: 'symbolsfont';
font-size: 0.8em;
display: inline-block;
margin-right: $interiorMargin;
height: $d;
width: $d;
left: 0;
top: 0;
position: absolute;
text-align: center;
}
}
&.no-text {
overflow: hidden;
margin-right: 0;
padding-left: 0;
height: $d;
width: $d;
em {
overflow: hidden;
}
}
input {
display: none;
&:checked ~ em:before {
background: $colorCheck;
color: lighten($colorCheck, 50%);
content: "2";
}
}
}
.input-labeled {
margin-left: $interiorMargin;
label {
display: inline-block;
margin-right: $interiorMarginSm;
}
&.inline {
display: inline-block;
}
&:first-child {
margin-left: 0;
}
margin-left: $interiorMargin;
label {
display: inline-block;
margin-right: $interiorMarginSm;
}
&.inline {
display: inline-block;
}
&:first-child {
margin-left: 0;
}
}
.s-menu-btn label.checkbox.custom {
margin-left: 5px;
margin-left: 5px;
}
.item .checkbox {
&.checked label {
@include box-shadow(none);
border-bottom: none;
}
&.checked label {
@include box-shadow(none);
border-bottom: none;
}
}
.context-available {
$c: $colorKey;
color: $c;
&:hover {
color: lighten($c, 10%);
}
$c: $colorKey;
color: $c;
&:hover {
color: lighten($c, 10%);
}
}
.view-switcher {
@include trans-prop-nice-fade($controlFadeMs);
@include trans-prop-nice-fade($controlFadeMs);
}
/******************************************************** OBJECT-HEADER */
.object-header {
//@include test();
font-size: 1em;
//@include test();
font-size: 1em;
//> .title-label,
//> .type-icon,
//> .context-available {
// //@include tmpBorder(#6666ff);
// //vertical-align: middle;
//}
> .type-icon {
color: $colorObjHdrIc;
font-size: 120%;
float: left;
margin-right: $interiorMargin;
}
> .type-icon {
color: $colorObjHdrIc;
font-size: 120%;
float: left;
margin-right: $interiorMargin;
}
.l-elem-wrapper {
//@include test(#66f, 0.2);
@include justify-content(flex-start);
mct-representation {
// Holds the context-available item
// Must have min-width to make flex work properly
// in Safari
min-width: 0.7em;
}
}
.l-elem-wrapper {
//@include test(#66f, 0.2);
@include webkitProp(justify-content, flex-start);
mct-representation {
// Holds the context-available item
// Must have min-width to make flex work properly
// in Safari
min-width: 0.7em;
}
}
.action {
margin-right: $interiorMargin;
}
.action {
margin-right: $interiorMargin;
}
.title-label {
//@include test(green, 0.9);
color: $colorObjHdrTxt;
@include ellipsize();
//color: pushBack($colorBodyFg, 40%);
@include webkitProp(flex, '0 1 auto');
padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text.
//position: relative;
}
.title-label {
//@include test(green, 0.9);
color: $colorObjHdrTxt;
@include ellipsize();
//color: pushBack($colorBodyFg, 40%);
@include webkitProp(flex, '0 1 auto');
padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text.
//position: relative;
}
.context-available {
font-size: 0.7em;
@include webkitProp(flex, '0 0 1');
//margin-right: $interiorMargin;
}
.context-available {
font-size: 0.7em;
@include webkitProp(flex, '0 0 1');
//margin-right: $interiorMargin;
}
@include desktop {
.context-available {
@include trans-prop-nice(opacity, 0.25s);
opacity: 0;
}
&:hover {
.context-available {
opacity: 1;
}
}
}
@include desktop {
.context-available {
@include trans-prop-nice(opacity, 0.25s);
opacity: 0;
}
&:hover {
.context-available {
opacity: 1;
}
}
}
}
/******************************************************** PROGRESS BAR */
@ -280,20 +273,20 @@ label.checkbox.custom {
.l-progress-bar {
// Assume will be determinate by default
display: inline-block;
overflow: hidden;
position: relative;
display: inline-block;
overflow: hidden;
position: relative;
.progress-amt-holder {
.progress-amt-holder {
@include absPosDefault(1px);
}
.progress-amt,
.progress-amt:before,
.progress-amt:after {
@include absPosDefault();
@include absPosDefault();
display: block;
content: '';
}
}
.progress-amt {
right: auto; // Allow inline width to control }
@ -311,10 +304,10 @@ label.checkbox.custom {
@include boxIncised(0.3, 4px);
background: $colorProgressBarOuter;
//border:1px solid $colorProgressBarOuter;
.progress-amt {
@include border-radius($basicCr);
@include boxShdw();
@include border-radius($basicCr - 1);
.progress-amt {
@include border-radius($basicCr);
@include boxShdw();
@include border-radius($basicCr - 1);
@include trans-prop-nice(width);
&:before {
background-color: $colorProgressBarAmt;
@ -325,7 +318,7 @@ label.checkbox.custom {
transparent 5%, rgba(#fff,0.25) 30%, transparent 100%
));
}
}
}
&:not(.indeterminate) {
.progress-amt:before {
@ -348,187 +341,185 @@ label.checkbox.custom {
/******************************************************** SLIDERS */
.slider {
$knobH: 100%; //14px;
.slot {
// @include border-radius($basicCr * .75);
//@include sliderTrack();
width: auto;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.knob {
//@include btnSubtle();
//@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid);
@include trans-prop-nice-fade(.25s);
background-color: $sliderColorKnob;
&:hover {
background-color: $sliderColorKnobHov;
}
position: absolute;
height: $knobH;
width: $sliderKnobW;
top: 0;
auto: 0;
bottom: auto;
left: auto;
}
.knob-l {
@include border-left-radius($sliderKnobW);
cursor: w-resize;
}
.knob-r {
@include border-right-radius($sliderKnobW);
cursor: e-resize;
}
.range {
@include trans-prop-nice-fade(.25s);
background-color: $sliderColorRange;
cursor: ew-resize;
position: absolute;
top: 0; //$tbOffset;
right: auto;
bottom: 0;
left: auto;
height: auto;
width: auto;
&:hover {
background-color: $sliderColorRangeHov;
}
}
$knobH: 100%; //14px;
.slot {
// @include border-radius($basicCr * .75);
//@include sliderTrack();
width: auto;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.knob {
@include trans-prop-nice-fade(.25s);
background-color: $sliderColorKnob;
&:hover {
background-color: $sliderColorKnobHov;
}
position: absolute;
height: $knobH;
width: $sliderKnobW;
top: 0;
auto: 0;
bottom: auto;
left: auto;
}
.knob-l {
@include border-left-radius($sliderKnobW);
cursor: w-resize;
}
.knob-r {
@include border-right-radius($sliderKnobW);
cursor: e-resize;
}
.range {
@include trans-prop-nice-fade(.25s);
background-color: $sliderColorRange;
cursor: ew-resize;
position: absolute;
top: 0; //$tbOffset;
right: auto;
bottom: 0;
left: auto;
height: auto;
width: auto;
&:hover {
background-color: $sliderColorRangeHov;
}
}
}
/******************************************************** DATETIME PICKER */
.l-datetime-picker {
$r1H: 15px;
@include user-select(none);
font-size: 0.8rem;
padding: $interiorMarginLg !important;
width: 230px;
.l-month-year-pager {
$pagerW: 20px;
//@include test();
//font-size: 0.8rem;
height: $r1H;
margin-bottom: $interiorMargin;
position: relative;
.pager,
.val {
//@include test(red);
@extend .abs;
}
.pager {
width: $pagerW;
@extend .ui-symbol;
&.prev {
right: auto;
&:before {
content: "\3c";
}
}
&.next {
left: auto;
text-align: right;
&:before {
content: "\3e";
}
}
}
.val {
text-align: center;
left: $pagerW + $interiorMargin;
right: $pagerW + $interiorMargin;
}
}
.l-calendar,
.l-time-selects {
border-top: 1px solid $colorInteriorBorder
}
.l-time-selects {
line-height: $formInputH;
}
$r1H: 15px;
@include user-select(none);
font-size: 0.8rem;
padding: $interiorMarginLg !important;
width: 230px;
.l-month-year-pager {
$pagerW: 20px;
//@include test();
//font-size: 0.8rem;
height: $r1H;
margin-bottom: $interiorMargin;
position: relative;
.pager,
.val {
//@include test(red);
@extend .abs;
}
.pager {
width: $pagerW;
@extend .ui-symbol;
&.prev {
right: auto;
&:before {
content: "\3c";
}
}
&.next {
left: auto;
text-align: right;
&:before {
content: "\3e";
}
}
}
.val {
text-align: center;
left: $pagerW + $interiorMargin;
right: $pagerW + $interiorMargin;
}
}
.l-calendar,
.l-time-selects {
border-top: 1px solid $colorInteriorBorder
}
.l-time-selects {
line-height: $formInputH;
}
}
/******************************************************** CALENDAR */
.l-calendar {
$colorMuted: pushBack($colorMenuFg, 30%);
ul.l-cal-row {
@include display-flex;
@include flex-flow(row nowrap);
margin-top: 1px;
&:first-child {
margin-top: 0;
}
li {
@include flex(1 0);
//@include test();
margin-left: 1px;
padding: $interiorMargin;
text-align: center;
&:first-child {
margin-left: 0;
}
}
&.l-header li {
color: $colorMuted;
}
&.l-body li {
@include trans-prop-nice(background-color, .25s);
cursor: pointer;
&.in-month {
background-color: $colorCalCellInMonthBg;
}
.sub {
color: $colorMuted;
font-size: 0.8em;
}
&.selected {
background: $colorCalCellSelectedBg;
color: $colorCalCellSelectedFg;
.sub {
color: inherit;
}
}
&:hover {
background-color: $colorCalCellHovBg;
color: $colorCalCellHovFg;
.sub {
color: inherit;
}
}
}
}
$colorMuted: pushBack($colorMenuFg, 30%);
ul.l-cal-row {
@include display-flex;
@include flex-flow(row nowrap);
margin-top: 1px;
&:first-child {
margin-top: 0;
}
li {
@include flex(1 0);
//@include test();
margin-left: 1px;
padding: $interiorMargin;
text-align: center;
&:first-child {
margin-left: 0;
}
}
&.l-header li {
color: $colorMuted;
}
&.l-body li {
@include trans-prop-nice(background-color, .25s);
cursor: pointer;
&.in-month {
background-color: $colorCalCellInMonthBg;
}
.sub {
color: $colorMuted;
font-size: 0.8em;
}
&.selected {
background: $colorCalCellSelectedBg;
color: $colorCalCellSelectedFg;
.sub {
color: inherit;
}
}
&:hover {
background-color: $colorCalCellHovBg;
color: $colorCalCellHovFg;
.sub {
color: inherit;
}
}
}
}
}
/******************************************************** BROWSER ELEMENTS */
@include desktop {
::-webkit-scrollbar {
@include border-radius(2px);
@include box-sizing(border-box);
@include box-shadow(inset $scrollbarTrackShdw);
background-color: $scrollbarTrackColorBg;
height: $scrollbarTrackSize;
width: $scrollbarTrackSize;
}
::-webkit-scrollbar {
@include border-radius(2px);
@include box-sizing(border-box);
@include box-shadow(inset $scrollbarTrackShdw);
background-color: $scrollbarTrackColorBg;
height: $scrollbarTrackSize;
width: $scrollbarTrackSize;
}
::-webkit-scrollbar-thumb {
$bg: $scrollbarThumbColor;
$hc: $scrollbarThumbColorHov;
$gr: 5%;
@include background-image(linear-gradient(lighten($bg, $gr), $bg 20px));
@include border-radius(2px);
@include box-sizing(border-box);
//@include boxShdwSubtle();
//border-top: 1px solid lighten($bg, 20%);
&:hover {
@include background-image(linear-gradient(lighten($hc, $gr), $hc 20px));
}
}
::-webkit-scrollbar-thumb {
$bg: $scrollbarThumbColor;
$hc: $scrollbarThumbColorHov;
$gr: 5%;
@include background-image(linear-gradient(lighten($bg, $gr), $bg 20px));
@include border-radius(2px);
@include box-sizing(border-box);
//@include boxShdwSubtle();
//border-top: 1px solid lighten($bg, 20%);
&:hover {
@include background-image(linear-gradient(lighten($hc, $gr), $hc 20px));
}
}
::-webkit-scrollbar-corner {
background: $scrollbarTrackColorBg;
}
}
::-webkit-scrollbar-corner {
background: $scrollbarTrackColorBg;
}
}

View File

@ -32,7 +32,7 @@
.l-tree-item-flat-list {
// For lists of tree-items that are flat. Remove margin, etc. normally needed for the expansion arrow.
.tree-item {
.label {
.t-object-label {
left: $interiorMargin !important;
}
}

View File

@ -43,6 +43,11 @@
}
&.create-btn {
&:before {
content:'\2b';
display: inline;
font-family: symbolsfont;
}
.title-label {
font-size: 1rem;
}
@ -83,7 +88,7 @@
@include menuUlReset();
li {
@include box-sizing(border-box);
border-top: 1px solid lighten($colorMenuBg, 20%);
border-top: 1px solid pullForward($colorMenuBg, 10%);
color: pullForward($colorMenuBg, 60%);
line-height: $menuLineH;
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;

View File

@ -19,68 +19,114 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.split-layout {
$b: pullForward($colorBodyBg, $contrastRatioPercent);
.splitter {
background-color: $b;
@include border-radius($splitterEndCr);
@include boxShdw($splitterShdw);
overflow: hidden;
position: absolute;
z-index: 1;
//@if $colorSplitterHover != 'none' {
&:hover {
background-color: $colorSplitterHover;
}
//}
}
&.horizontal {
// Slides vertically up and down, splitting the element horizontally
overflow: hidden; // Suppress overall scroll; each internal pane handles its own overflow
.pane {
left: 0;
right: 0;
&.top {
bottom: auto;
}
&.bottom {
top: auto;
}
}
>.splitter {
@include controlGrippy($colorSplitterInterior, horizontal);
cursor: row-resize;
left: 0; right: 0;
width: auto;
height: $splitterW;
}
}
&.vertical {
// Slides horizontally left to right, splitting the element vertically
.pane {
top: 0;
bottom: 0;
&.left {
right: auto;
}
&.right {
left: auto;
}
}
>.splitter {
@include controlGrippy($colorBodyBg, vertical);
bottom: 0;
cursor: col-resize;
width: $splitterW;
}
}
.splitter {
// Redo the splitter.
// New look is a simple line.
// Main width is used to provide a good click area, and is always transparent
// :after will be a positioned and colored element that is the handle
//@include test(red);
display: block;
position: absolute;
z-index: 1;
&:after {
// The handle
content:"";
pointer-events: none;
@include absPosDefault(0);
background: $colorSplitterBg;
display: block;
@if $splitterEndCr != 'none' {
@include border-radius($splitterEndCr);
}
}
&:active {
//@include test();
&:after {
background-color: $colorSplitterActive !important;
}
}
@if $colorSplitterHover != 'none' {
&:not(:active) {
&:hover {
&:after {
background-color: $colorSplitterHover !important;
@include trans-prop-nice(background-color, 150ms);
}
}
}
}
}
.browse-area .splitter {
top: $ueTopBarH + $interiorMarginLg;
.split-layout {
$inset: splitterHandleInset($splitterD,$splitterHandleD);
&.horizontal {
// Slides vertically up and down, splitting the element horizontally
overflow: hidden; // Suppress overall scroll; each internal pane handles its own overflow
.pane {
left: 0;
right: 0;
&.top {
bottom: auto;
}
&.bottom {
top: auto;
}
}
>.splitter {
cursor: row-resize;
left: 0;
right: 0;
height: $splitterD;
&:after {
top: $inset; bottom: $inset;
}
}
}
&.vertical {
// Slides horizontally left to right, splitting the element vertically
.pane {
top: 0;
bottom: 0;
&.left {
right: auto;
}
&.right {
left: auto;
}
}
>.splitter {
cursor: col-resize;
top: 0;
bottom: 0;
&:not(.flush-right) {
width: $splitterD;
&:after {
left: $inset; right: $inset;
}
}
&.flush-right {
width: ceil($splitterD / 2);
&:after {
background-color: transparent;
left: auto; right: 0; width: $splitterHandleD;
}
&.edge-shdw {
@include background-image(linear-gradient(90deg, rgba(black, 0) 40%, rgba(black, 0.05) 70%, rgba(black, 0.2) 100%));
}
}
}
}
}
/*.browse-area .splitter {
top: 0; //$ueTopBarH + $interiorMarginLg;
}
.edit-area .splitter {
top: 0;
}
}*/

View File

@ -86,27 +86,16 @@
//top: $ueBrowseGridItemTopBarH; bottom: $ueBrowseGridItemBottomBarH; //
line-height: $lh;
z-index: 1;
.item-type {
//@include trans-prop-nice("color", $transTime);
@include absPosDefault($iconMargin, false);
//@include test(red);
//color: $colorItemIc;
text-align: center;
.item-type,
.t-item-icon {
//@include test();
@include transform(translateX(-50%) translateY(-55%));
position: absolute;
top: 50%; left: 50%;
//height: $iconD; width: $iconD;
font-size: $iconD * 0.95; //6em;
line-height: $iconD;
bottom: auto;
height: $iconD;
top: $iconMargin - 10;
.l-icon-link {
color: $colorIconLink;
height: auto;
line-height: 100%;
position: absolute;
font-size: 0.3em;
left: 0px;
bottom: 10px;
z-index: 2;
}
//line-height: normal;
//text-align: center;
}
.item-open {
@include trans-prop-nice("opacity", $transTime);

View File

@ -49,18 +49,11 @@
}
.item-main {
.item-type {
//@include test(blue);
.item-type,
.t-item-icon {
font-size: $mobileListIconSize;
right: auto;
bottom: auto;
left: 0;
line-height: 100%;
text-align: left;
width: $mobileListIconSize;
.l-icon-link {
bottom: 0;
}
left: $interiorMarginLg + $interiorMargin;
line-height: normal;
}
.item-open {
display: block;

View File

@ -32,7 +32,7 @@
background-color: $colorMobilePaneLeft;
}
.pane.right-repr {
.pane.right.items {
//@include test();
@include slMenuTransitions;
margin-left: 0 !important;
@ -42,78 +42,66 @@
}
}
.user-environ .browse-area,
.user-environ .edit-area,
.user-environ .editor {
top: 0; left: 0; right: 0; bottom: $ueFooterH;
}
.holder.l-mobile {
top: $bodyMargin !important;
.holder.holder-create-and-search {
right: $bodyMargin !important;
bottom: $bodyMargin !important;
left: $bodyMargin !important;
}
// When the tree is hidden, these are the
// When the tree is hidden, these are the
// classes used for the left menu and the
// right representation.
.browse-hidetree {
@include user-select(none);
.pane-tree-hidden {
// Sets the left tree menu when the tree
// is hidden.
.pane.left.treeview {
opacity: 0;
right: 100% !important;
width: auto !important;
overflow-y: hidden;
overflow-x: hidden;
@include trans-prop-nice(opacity, 150ms);
//right: 100% !important;
//width: auto !important;
//overflow-y: hidden;
//overflow-x: hidden;
opacity: 0 !important;
}
// Sets the right represenation when
// the tree is hidden.
.pane.right-repr {
.pane.right.items {
left: 0 !important;
}
}
.browse-showtree {
.pane-tree-showing {
// NOTE: DISABLED SELECTION
// Selection disabled in both panes
// causing cut/copy/paste menu to
// not appear. Should me moved in
// future to properly work
@include user-select(none);
//@include user-select(none);
// Sets the left tree menu when the tree is shown.
.pane.left.treeview {
@include trans-prop-nice(opacity, .4s);
@include trans-prop-nice(opacity, 250ms, $delay: 250ms);
@include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%));
opacity: 1;
display: block !important;
//width: auto !important; // CH CO
right: auto !important;
width: $proporMenuWithView !important;
}
// Sets the right representation when the tree is shown.
.pane.right-repr {
.pane.right.items {
left: $proporMenuWithView !important;
//width: auto !important;
//left: 0 !important;
//transform: translateX($proporMenuWithView);
}
}
.mobile-menu-icon {
.toggle-tree {
color: $colorKey !important;
font-size: 110%;
position: absolute;
top: $bodyMargin + 2;
left: $bodyMargin;
&:after {
content:'m' !important;
font-family: symbolsfont;
}
}
.object-browse-bar {
//@include test();
left: 30px !important;
left: 45px !important;
.context-available {
opacity: 1 !important;
}
@ -153,13 +141,13 @@
}
@include phonePortrait {
.browse-showtree {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right-repr {
.pane.right.items {
left: 0 !important;
@include webkitProp(transform, translateX($proporMenuOnly));
@include transform(translateX($proporMenuOnly));
#content-area {
opacity: 0;
}

View File

@ -37,21 +37,18 @@
//@include test(red);
position: absolute;
font-size: 1.1em;
height: $mobileTreeItemH;
line-height: inherit;
right: 0px;
width: $mobileTreeRightArrowW;
text-align: center;
}
.label {
.label,
.t-object-label {
left: 0;
right: $mobileTreeRightArrowW + $interiorMargin; // Allows tree item name to stop prior to the arrow
line-height: $mobileTreeItemH;
//font-size: 1.1em; // CH CO
.type-icon {
@include verticalCenterBlock($mobileTreeItemH, $treeTypeIconH);
}
.title-label {
}
line-height: inherit;
}
}
}

View File

@ -52,7 +52,6 @@ ul.tree {
font-size: 0.75em;
width: $treeVCW;
$runningItemW: $interiorMargin + $treeVCW;
// NOTE: [Mobile] Removed Hover on Mobile
@include desktop {
&:hover {
color: $colorItemTreeVCHover !important;
@ -60,25 +59,34 @@ ul.tree {
}
}
.label {
.label,
.t-object-label {
display: block;
// @include test(orange);
@include absPosDefault();
//left: $runningItemW + $interiorMargin; // Adding pad to left to make room for link icon
line-height: $menuLineH;
//left: $runningItemW;
.t-item-icon {
@include txtShdwSubtle($shdwItemTreeIcon);
font-size: $treeTypeIconH;
color: $colorItemTreeIcon;
position: absolute;
left: $interiorMargin;
top: 50%;
width: $treeTypeIconH;
@include transform(translateY(-50%));
}
.type-icon {
//@include absPosDefault(0, false);
$d: $treeTypeIconH; // 16px is crisp size
$d: $treeTypeIconH;
@include txtShdwSubtle($shdwItemTreeIcon);
font-size: $d;
font-size: $treeTypeIconH;
color: $colorItemTreeIcon;
left: $interiorMargin;
position: absolute;
@include verticalCenterBlock($menuLineHPx, $d);
@include verticalCenterBlock($menuLineHPx, $treeTypeIconHPx);
line-height: 100%;
right: auto; width: $d;
right: auto; width: $treeTypeIconH;
.icon {
&.l-icon-link,
@ -100,7 +108,8 @@ ul.tree {
}
}
}
.title-label {
.title-label,
.t-title-label {
@include absPosDefault();
display: block;
left: $runningItemW + ($interiorMargin * 3);
@ -116,7 +125,7 @@ ul.tree {
.view-control {
color: $colorItemTreeSelectedVC;
}
.label .type-icon {
.t-object-label .t-item-icon {
color: $colorItemTreeSelectedFg; //$colorItemTreeIconHover;
}
}
@ -125,9 +134,9 @@ ul.tree {
// NOTE: [Mobile] Removed Hover on Mobile
@include desktop {
&:hover {
background: rgba($colorBodyFg, 0.1); //lighten($colorBodyBg, 5%);
color: pullForward($colorBodyFg, 20%);
.icon {
background: $colorItemTreeHoverBg;
color: $colorItemTreeHoverFg;
.t-item-icon {
color: $colorItemTreeIconHover;
}
}
@ -152,7 +161,7 @@ ul.tree {
}
.tree-item {
.label {
.t-object-label {
left: $interiorMargin + $treeVCW;
}
}

View File

@ -29,13 +29,13 @@
}
}
.holder-all {
/*.holder-all {
$myM: 0; // $interiorMarginSm;
top: $myM;
right: $myM;
bottom: $myM;
left: $myM;
}
}*/
.browse-area,
.edit-area,
@ -96,12 +96,8 @@
.user-environ {
.browse-area,
.edit-area,
.editor {
top: $bodyMargin + $ueTopBarH + ($interiorMargin);
right: $bodyMargin;
bottom: $ueFooterH + $bodyMargin;
left: $bodyMargin;
top: 0; left: 0; right: 0; bottom: $ueFooterH;
}
.browse-area,
@ -115,31 +111,51 @@
.edit-area {
$tbH: $btnToolbarH + $interiorMargin;
top: $bodyMargin + $ueTopBarEditH + ($interiorMargin);
left: $bodyMargin;
right: $bodyMargin;
bottom: $bodyMargin + $ueFooterH;
.tool-bar {
bottom: auto;
height: $tbH;
line-height: $btnToolbarH;
}
.work-area {
.object-holder.work-area {
top: $tbH + $interiorMargin * 2;
overflow: auto;
}
}
.ue-bottom-bar {
//@include absPosDefault($bodyMargin);
@include absPosDefault(0); // New status bar design
top: auto;
height: $ueFooterH;
.status-holder {
//right: $ueAppLogoW + $bodyMargin; New status bar design
z-index: 1;
}
.app-logo {
left: auto;
width: $ueAppLogoW;
z-index: 2;
}
}
// from _bottom-bar.scss
.ue-bottom-bar {
@include absPosDefault(0);// New status bar design
top: auto;
height: $ueFooterH;
line-height: $ueFooterH - ($interiorMargin * 2);
background: $colorFooterBg;
color: lighten($colorBodyBg, 30%);
font-size: .7rem;
.status-holder {
@include box-sizing(border-box);
@include absPosDefault($interiorMargin);
@include ellipsize();
//line-height: $ueFooterH - ($interiorMargin * 2);
right: 120px;
text-transform: uppercase;
z-index: 1;
}
.app-logo {
@include box-sizing(border-box);
@include absPosDefault($interiorMargin);
cursor: pointer;
left: auto;
width: $ueAppLogoW;
z-index: 2;
&.logo-openmctweb {
background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center;
}
}
}
}
.cols {
@ -205,10 +221,19 @@
.browse-mode {
.split-layout {
.split-pane-component.pane.left {
min-width: 150px;
max-width: 800px;
width: $ueBrowseLeftPaneW;
.split-pane-component.pane {
//@include test(green);
&.treeview.left {
min-width: 150px;
max-width: 800px;
width: $ueBrowseLeftPaneTreeW;
}
&.t-inspect.right {
min-width: 200px;
max-width: 600px;
//padding-left: $ueCollapsedPaneEdgeM; // Allow room for mini-tab element
width: $ueBrowseRightPaneInspectW;
}
}
}
}
@ -226,16 +251,33 @@
}
.pane {
@include box-sizing(border-box);
position: absolute;
.pane-header {
text-transform: uppercase;
height: $ueTopBarH;
line-height: $ueTopBarH;
margin-bottom: $interiorMargin;
}
.primary-pane {
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
z-index: 2;
}
&.treeview.left {
.create-btn-holder {
bottom: auto;
top: 0;
height: $ueTopBarH;
.wrapper.menu-element {
position: absolute;
bottom: $interiorMargin;
}
//.create-btn-holder {
// //bottom: auto;
// //top: 0;
// height: $ueTopBarH;
// .wrapper.menu-element {
// position: absolute;
// bottom: $interiorMargin;
// }
//}
.holder-create-and-search{
}
.search-holder {
top: $ueTopBarH + $interiorMarginLg;
@ -245,6 +287,54 @@
top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin;
}
}
.mini-tab-icon.toggle-pane {
//@include test(blue, 0.3);
z-index: 5;
@include desktop {
$d: $uePaneMiniTabH;
$paneExpandedOffset: $splitterD + $uePaneMiniTabW;
top: $bodyMargin;
height: $d;
line-height: $d;
&:after {
// Always the icon that shows when the pane is collapsed
opacity: 0;
}
&.collapsed {
&:before {
opacity: 0;
}
&:after {
opacity: 1;
}
}
&.toggle-tree.anchor-left {
left: 0;
@include transform(translateX(-1 * $paneExpandedOffset));
&:after {
content: '\6d'; // Menu 'hamburger' icon
}
&.collapsed {
left: 0;
@include transform(translateX((-1 * $ueCollapsedPaneEdgeM) + $interiorMargin));
}
&:not(.collapsed):before {
@include trans-prop-nice(opacity, 200ms, 200ms);
}
}
&.toggle-inspect.anchor-right {
right: $bodyMargin;
&:after {
content: '\e615'; // e615: Crosshair icon; was e608: Info "i" icon
}
&.collapsed {
right: $interiorMargin;
}
}
}
}
&.items {
.object-browse-bar {
.left.abs,
@ -266,32 +356,50 @@
}
}
}
&.vertical {
/* &.vertical {
// Slides left and right
> .pane {
// @include test();
margin-left: $interiorMargin;
> .pane.left {
> .holder {
left: 0;
right: 0;
}
&:first-child {
margin-left: 0;
.holder {
right: $interiorMarginSm;
}
left: $bodyMargin;
}
}
> .pane.right {
> .holder {
right: $bodyMargin;
}
}
}*/
// Specific elements margins
.holder.holder-create-and-search {
top: $bodyMargin;
right: 0;
bottom: $bodyMargin;
left: $bodyMargin;
}
.holder.holder-object-and-inspector {
top: 0;
right: 0;
bottom: 0;
left: 0;
.holder-object {
top: $bodyMargin;
bottom: $bodyMargin;
}
.holder-inspector-elements {
top: $bodyMargin;
bottom: $bodyMargin;
left: $bodyMargin;
right: $bodyMargin;
}
}
}
.object-holder {
overflow: hidden; // Contained objects need to handle their own overflow now
@include absPosDefault(0, auto);
top: $ueTopBarH + $interiorMarginLg;
> ng-include {
@include absPosDefault(0, auto);
}
&.l-controls-visible {
&.l-time-controller-visible {
bottom: nth($ueTimeControlH,1) + nth($ueTimeControlH,2) +nth($ueTimeControlH,3) + ($interiorMargin * 3);
@ -343,4 +451,84 @@
@include webkitProp(flex, '1 1 0');
padding-right: $interiorMarginLg;
}
}
}
// When the tree is hidden, these are the
// classes used for the left menu and the
// right representation.
.pane-tree-hidden {
// Sets the left tree menu when the tree is hidden.
//.pane.left.treeview,
.tree-holder,
.splitter-treeview,
.holder-create-and-search {
opacity: 0;
}
/*.holder-create-and-search {
@include trans-prop-nice((top, left), 250ms);
top: $ueTopBarH + $interiorMargin;
left: -1 * $bodyMargin !important;
.create-btn {
@include border-left-radius(0);
@include trans-prop-nice((width), 250ms);
width: $uePaneMiniTabW !important;
text-align: center !important;
padding: 0;
.title-label,
&:after {
display: none;
}
&:before {
font-size: 9px;
}
}
}*/
}
.pane-tree-showing {
// Sets the left tree menu when the tree is shown.
//.pane.left.treeview,
.tree-holder,
.splitter-treeview {
@include trans-prop-nice(opacity, $dur: 250ms, $delay: 250ms);
opacity: 1;
}
.holder-create-and-search {
@include trans-prop-nice(opacity, $dur: 250ms, $delay: 200ms);
}
}
.pane-inspect-showing {
.l-object-and-inspector {
.l-inspect,
.splitter-inspect {
@include trans-prop-nice(opacity, $dur: 250ms, $delay: 250ms);
opacity: 1;
}
}
}
.pane-inspect-hidden {
.l-object-and-inspector {
.l-inspect,
.splitter-inspect {
opacity: 0;
}
}
}
@include desktop {
.pane.treeview.left .tree-holder {
padding-right: $interiorMargin;
}
.pane-tree-hidden {
.pane.right.primary-pane { left: $ueCollapsedPaneEdgeM !important; }
}
.pane-inspect-hidden .l-object-and-inspector {
.pane.left { right: $ueCollapsedPaneEdgeM !important; }
}
.pane:not(.resizing) {
@include trans-prop-nice-resize-w(250ms);
}
}

View File

@ -0,0 +1,20 @@
<span class="s-btn"
ng-controller="DateTimeFieldController">
<input type="text"
ng-model="textValue"
ng-class="{ error: textInvalid }">
</input>
<a class="ui-symbol icon icon-calendar"
ng-if="structure.format === 'utc' || !structure.format"
ng-click="pickerActive = !pickerActive">
</a>
<mct-popup ng-if="pickerActive">
<div mct-click-elsewhere="pickerActive = false">
<mct-control key="'datetime-picker'"
ng-model="ngModel"
field="field"
options="{ hours: true }">
</mct-control>
</div>
</mct-popup>
</span>

View File

@ -22,47 +22,24 @@
<div ng-controller="TimeRangeController">
<div class="l-time-range-inputs-holder">
<span class="l-time-range-inputs-elem ui-symbol type-icon">&#x43;</span>
<span class="l-time-range-input" ng-controller="ToggleController as t1">
<!--<span class="lbl">Start</span>-->
<span class="s-btn time-range-start">
<input type="text"
ng-model="boundsModel.start"
ng-class="{ error: !boundsModel.startValid }">
</input>
<a class="ui-symbol icon icon-calendar" ng-click="t1.toggle()"></a>
<mct-popup ng-if="t1.isActive()">
<div mct-click-elsewhere="t1.setState(false)">
<mct-control key="'datetime-picker'"
ng-model="ngModel.outer"
field="'start'"
options="{ hours: true }">
</mct-control>
</div>
</mct-popup>
</span>
<span class="l-time-range-input">
<mct-control key="'datetime-field'"
structure="{ format: parameters.format }"
ng-model="ngModel.outer"
field="'start'"
class="time-range-start">
</mct-control>
</span>
<span class="l-time-range-inputs-elem lbl">to</span>
<span class="l-time-range-input" ng-controller="ToggleController as t2">
<!--<span class="lbl">End</span>-->
<span class="s-btn l-time-range-input">
<input type="text"
ng-model="boundsModel.end"
ng-class="{ error: !boundsModel.endValid }">
</input>
<a class="ui-symbol icon icon-calendar" ng-click="t2.toggle()">
</a>
<mct-popup ng-if="t2.isActive()">
<div mct-click-elsewhere="t2.setState(false)">
<mct-control key="'datetime-picker'"
ng-model="ngModel.outer"
field="'end'"
options="{ hours: true }">
</mct-control>
</div>
</mct-popup>
</span>&nbsp;
<mct-control key="'datetime-field'"
structure="{ format: parameters.format }"
ng-model="ngModel.outer"
field="'end'"
class="time-range-end">
</mct-control>&nbsp;
</span>
</div>
@ -97,7 +74,7 @@
<div class="l-time-range-ticks-holder">
<div class="l-time-range-ticks">
<div
ng-repeat="tick in ticks"
ng-repeat="tick in ticks track by $index"
ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
class="tick tick-x"
>

View File

@ -19,16 +19,7 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<span class="label s-label">
<span class='ui-symbol icon type-icon'>
{{type.getGlyph()}}
<span
class='ui-symbol icon l-icon-link'
ng-show="location.isLink()"
></span>
<span class='ui-symbol icon l-icon-alert'></span>
</span>
<span class='title-label'>
{{model.name}}
</span>
<span class="t-object-label">
<span class="t-item-icon" ng-class="{ 'l-icon-link':location.isLink() }">{{type.getGlyph()}}</span>
<span class='t-title-label'>{{model.name}}</span>
</span>

View File

@ -0,0 +1,63 @@
<!--
Open MCT Web, Copyright (c) 2014-2015, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT Web is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT Web includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<span class="l-inspect" ng-controller="ObjectInspectorController as controller">
<div class="abs holder holder-inspector-elements">
<div class="pane-header">Inspection</div>
<ul>
<li>
<em>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>
</li>
<li ng-if="contextutalParents.length > 0">
<em title="The location of this linked object.">Location</em>
<span class="inspector-location"
ng-repeat="parent in contextutalParents"
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item">
</mct-representation>
</span>
</li>
<li ng-if="primaryParents.length > 0">
<em title="The location of the original object that this was linked from.">Original Location</em>
<span class="inspector-location"
ng-repeat="parent in primaryParents"
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item">
</mct-representation>
</span>
</li>
</ul>
</div>
</span>

View File

@ -0,0 +1,79 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
define(
[],
function () {
'use strict';
/**
* Controller to support the date-time entry field.
*
* Accepts a `format` property in the `structure` attribute
* which allows a date/time to be specified via its symbolic
* key (as will be used to look up said format from the
* `formatService`.)
*
* {@see FormatService}
* @constructor
* @memberof platform/commonUI/general
* @param $scope the Angular scope for this controller
* @param {FormatService} formatService the service to user to format
* domain values
* @param {string} defaultFormat the format to request when no
* format has been otherwise specified
*/
function DateTimeFieldController($scope, formatService, defaultFormat) {
var formatter = formatService.getFormat(defaultFormat);
function updateFromModel(value) {
// Only reformat if the value is different from user
// input (to avoid reformatting valid input while typing.)
if (!formatter.validate($scope.textValue) ||
formatter.parse($scope.textValue) !== value) {
$scope.textValue = formatter.format(value);
$scope.textInvalid = false;
}
}
function updateFromView(textValue) {
$scope.textInvalid = !formatter.validate(textValue);
if (!$scope.textInvalid) {
$scope.ngModel[$scope.field] =
formatter.parse(textValue);
}
}
function setFormat(format) {
formatter = formatService.getFormat(format || defaultFormat);
updateFromModel($scope.ngModel[$scope.field]);
}
$scope.$watch('structure.format', setFormat);
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watch('textValue', updateFromView);
}
return DateTimeFieldController;
}
);

View File

@ -0,0 +1,117 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining ObjectInspectorController. Created by shale on 08/21/2015.
*/
define(
[],
function () {
"use strict";
/**
* The ObjectInspectorController gets and formats the data for
* the inspector display
*
* @constructor
*/
function ObjectInspectorController($scope, objectService) {
$scope.primaryParents = [];
$scope.contextutalParents = [];
//$scope.isLink = false;
// Gets an array of the contextual parents/anscestors of the selected object
function getContextualPath() {
var currentObj = $scope.ngModel.selectedObject,
currentParent,
parents = [];
currentParent = currentObj &&
currentObj.hasCapability('context') &&
currentObj.getCapability('context').getParent();
while (currentParent && currentParent.getModel().type !== 'root' &&
currentParent.hasCapability('context')) {
// Record this object
parents.unshift(currentParent);
// Get the next one up the tree
currentObj = currentParent;
currentParent = currentObj.getCapability('context').getParent();
}
$scope.contextutalParents = parents;
}
// Gets an array of the parents/anscestors of the selected object's
// primary location (locational of original non-link)
function getPrimaryPath(current) {
var location;
// If this the the initial call of this recursive function
if (!current) {
current = $scope.ngModel.selectedObject;
$scope.primaryParents = [];
}
location = current.getModel().location;
if (location && location !== 'root') {
objectService.getObjects([location]).then(function (obj) {
var next = obj[location];
$scope.primaryParents.unshift(next);
getPrimaryPath(next);
});
}
}
// Gets the metadata for the selected object
function getMetadata() {
$scope.metadata = $scope.ngModel.selectedObject &&
$scope.ngModel.selectedObject.hasCapability('metadata') &&
$scope.ngModel.selectedObject.useCapability('metadata');
}
// Set scope variables when the selected object changes
$scope.$watch('ngModel.selectedObject', function () {
$scope.isLink = $scope.ngModel.selectedObject &&
$scope.ngModel.selectedObject.hasCapability('location') &&
$scope.ngModel.selectedObject.getCapability('location').isLink();
if ($scope.isLink) {
getPrimaryPath();
getContextualPath();
} else {
$scope.primaryParents = [];
getContextualPath();
}
getMetadata();
});
}
return ObjectInspectorController;
}
);

View File

@ -26,33 +26,32 @@ define(
function (moment) {
"use strict";
var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
TICK_SPACING_PX = 150;
var TICK_SPACING_PX = 150;
/**
* Controller used by the `time-controller` template.
* @memberof platform/commonUI/general
* @constructor
* @param $scope the Angular scope for this controller
* @param {FormatService} formatService the service to user to format
* domain values
* @param {string} defaultFormat the format to request when no
* format has been otherwise specified
* @param {Function} now a function to return current system time
*/
function TimeConductorController($scope, now) {
function TimeRangeController($scope, formatService, defaultFormat, now) {
var tickCount = 2,
innerMinimumSpan = 1000, // 1 second
outerMinimumSpan = 1000 * 60 * 60, // 1 hour
initialDragValue;
initialDragValue,
formatter = formatService.getFormat(defaultFormat);
function formatTimestamp(ts) {
return moment.utc(ts).format(DATE_FORMAT);
return formatter.format(ts);
}
function parseTimestamp(text) {
var m = moment.utc(text, DATE_FORMAT);
if (m.isValid()) {
return m.valueOf();
} else {
throw new Error("Could not parse " + text);
}
}
// From 0.0-1.0 to "0%"-"1%"
// From 0.0-1.0 to "0%"-"100%"
function toPercent(p) {
return (100 * p) + "%";
}
@ -101,41 +100,15 @@ define(
return { start: bounds.start, end: bounds.end };
}
function updateBoundsTextForProperty(ngModel, property) {
try {
if (!$scope.boundsModel[property] ||
parseTimestamp($scope.boundsModel[property]) !==
ngModel.outer[property]) {
$scope.boundsModel[property] =
formatTimestamp(ngModel.outer[property]);
}
} catch (e) {
// User-entered text is invalid, so leave it be
// until they fix it.
}
}
function updateBoundsText(ngModel) {
updateBoundsTextForProperty(ngModel, 'start');
updateBoundsTextForProperty(ngModel, 'end');
}
function updateViewFromModel(ngModel) {
var t = now();
ngModel = ngModel || {};
ngModel.outer = ngModel.outer || defaultBounds();
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
// First, dates for the date pickers for outer bounds
updateBoundsText(ngModel);
// Then various updates for the inner span
updateViewForInnerSpanFromModel(ngModel);
// Stick it back is scope (in case we just set defaults)
$scope.ngModel = ngModel;
updateViewForInnerSpanFromModel(ngModel);
updateTicks();
}
@ -155,7 +128,8 @@ define(
}
function toMillis(pixels) {
var span = $scope.ngModel.outer.end - $scope.ngModel.outer.start;
var span =
$scope.ngModel.outer.end - $scope.ngModel.outer.start;
return (pixels / $scope.spanWidth) * span;
}
@ -243,36 +217,10 @@ define(
updateTicks();
}
function updateStartFromText(value) {
try {
updateOuterStart(parseTimestamp(value));
updateBoundsTextForProperty($scope.ngModel, 'end');
$scope.boundsModel.startValid = true;
} catch (e) {
$scope.boundsModel.startValid = false;
return;
}
}
function updateEndFromText(value) {
try {
updateOuterEnd(parseTimestamp(value));
updateBoundsTextForProperty($scope.ngModel, 'start');
$scope.boundsModel.endValid = true;
} catch (e) {
$scope.boundsModel.endValid = false;
return;
}
}
function updateStartFromPicker(value) {
updateOuterStart(value);
updateBoundsText($scope.ngModel);
}
function updateEndFromPicker(value) {
updateOuterEnd(value);
updateBoundsText($scope.ngModel);
function updateFormat(key) {
formatter = formatService.getFormat(key || defaultFormat);
updateViewForInnerSpanFromModel($scope.ngModel);
updateTicks();
}
$scope.startLeftDrag = startLeftDrag;
@ -282,21 +230,18 @@ define(
$scope.rightDrag = rightDrag;
$scope.middleDrag = middleDrag;
$scope.state = false;
$scope.ticks = [];
$scope.boundsModel = {};
// Initialize scope to defaults
updateViewFromModel($scope.ngModel);
$scope.$watchCollection("ngModel", updateViewFromModel);
$scope.$watch("spanWidth", updateSpanWidth);
$scope.$watch("ngModel.outer.start", updateStartFromPicker);
$scope.$watch("ngModel.outer.end", updateEndFromPicker);
$scope.$watch("boundsModel.start", updateStartFromText);
$scope.$watch("boundsModel.end", updateEndFromText);
$scope.$watch("ngModel.outer.start", updateOuterStart);
$scope.$watch("ngModel.outer.end", updateOuterEnd);
$scope.$watch("parameters.format", updateFormat);
}
return TimeConductorController;
return TimeRangeController;
}
);

View File

@ -132,10 +132,10 @@ define(
// Get actual size (to obey min-width etc.)
firstSize = getSize(first[0]);
first.css(anchor.dimension, firstSize + 'px');
splitter.css(anchor.edge, (firstSize + splitterSize) + 'px');
splitter.css(anchor.edge, firstSize + 'px');
splitter.css(anchor.opposite, "auto");
last.css(anchor.edge, (firstSize + splitterSize * 3) + 'px');
last.css(anchor.edge, (firstSize + splitterSize) + 'px');
last.css(anchor.opposite, "0px");
position = firstSize + splitterSize;
@ -178,6 +178,12 @@ define(
return position;
}
// Dynamically apply a CSS class to elements when the user
// is actively resizing
function toggleClass(classToToggle) {
$element.children().toggleClass(classToToggle);
}
// Make sure anchor parameter is something we know
if (!ANCHORS[anchorKey]) {
$log.warn(ANCHOR_WARNING_MESSAGE);
@ -208,6 +214,7 @@ define(
// Interface exposed by controller, for mct-splitter to user
return {
position: getSetPosition,
toggleClass: toggleClass,
anchor: function () {
return anchor;
}

View File

@ -29,7 +29,8 @@ define(
// Pixel width to allocate for the splitter itself
var SPLITTER_TEMPLATE = "<div class='abs'" +
"mct-drag-down=\"splitter.startMove()\" " +
"mct-drag=\"splitter.move(delta)\"></div>",
"mct-drag=\"splitter.move(delta)\" " +
"mct-drag-up=\"splitter.endMove()\"></div>",
OFFSETS_BY_EDGE = {
left: "offsetLeft",
right: "offsetRight",
@ -53,6 +54,7 @@ define(
startMove: function () {
var splitter = element[0];
initialPosition = mctSplitPane.position();
mctSplitPane.toggleClass('resizing');
},
// Handle user changes to splitter position
move: function (delta) {
@ -63,6 +65,11 @@ define(
// Update the position of this splitter
mctSplitPane.position(initialPosition + pixelDelta);
},
// Grab the event when the user is done moving
// the splitter and pass it on
endMove: function() {
mctSplitPane.toggleClass('resizing');
}
};
}

View File

@ -0,0 +1,183 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../../src/controllers/DateTimeFieldController", "moment"],
function (DateTimeFieldController, moment) {
'use strict';
var TEST_FORMAT = "YYYY-MM-DD HH:mm:ss";
describe("The DateTimeFieldController", function () {
var mockScope,
mockFormatService,
mockFormat,
controller;
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1](value);
}
});
}
beforeEach(function () {
mockScope = jasmine.createSpyObj('$scope', ['$watch']);
mockFormatService =
jasmine.createSpyObj('formatService', ['getFormat']);
mockFormat = jasmine.createSpyObj('format', [
'parse',
'validate',
'format'
]);
mockFormatService.getFormat.andReturn(mockFormat);
mockFormat.validate.andCallFake(function (text) {
return moment.utc(text, TEST_FORMAT).isValid();
});
mockFormat.parse.andCallFake(function (text) {
return moment.utc(text, TEST_FORMAT).valueOf();
});
mockFormat.format.andCallFake(function (value) {
return moment.utc(value).format(TEST_FORMAT);
});
mockScope.ngModel = { testField: 12321 };
mockScope.field = "testField";
mockScope.structure = { format: "someFormat" };
controller = new DateTimeFieldController(
mockScope,
mockFormatService
);
});
it("updates models from user-entered text", function () {
var newText = "1977-05-25 17:30:00";
mockScope.textValue = newText;
fireWatch("textValue", newText);
expect(mockScope.ngModel.testField)
.toEqual(mockFormat.parse(newText));
expect(mockScope.textInvalid).toBeFalsy();
});
it("updates text from model values", function () {
var testTime = mockFormat.parse("1977-05-25 17:30:00");
mockScope.ngModel.testField = testTime;
fireWatch("ngModel[field]", testTime);
expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
});
describe("when user input is invalid", function () {
var newText, oldValue;
beforeEach(function () {
newText = "Not a date";
oldValue = mockScope.ngModel.testField;
mockScope.textValue = newText;
fireWatch("textValue", newText);
});
it("displays error state", function () {
expect(mockScope.textInvalid).toBeTruthy();
});
it("does not modify model state", function () {
expect(mockScope.ngModel.testField).toEqual(oldValue);
});
it("does not modify user input", function () {
expect(mockScope.textValue).toEqual(newText);
});
});
it("does not modify valid but irregular user input", function () {
// Don't want the controller "fixing" bad or
// irregularly-formatted input out from under
// the user's fingertips.
var newText = "2015-3-3 01:02:04",
oldValue = mockScope.ngModel.testField;
mockFormat.validate.andReturn(true);
mockFormat.parse.andReturn(42);
mockScope.textValue = newText;
fireWatch("textValue", newText);
expect(mockScope.textValue).toEqual(newText);
expect(mockScope.ngModel.testField).toEqual(42);
expect(mockScope.ngModel.testField).not.toEqual(oldValue);
});
it("obtains a format from the format service", function () {
fireWatch('structure.format', mockScope.structure.format);
expect(mockFormatService.getFormat)
.toHaveBeenCalledWith(mockScope.structure.format);
});
it("throws an error for unknown formats", function () {
mockFormatService.getFormat.andReturn(undefined);
expect(function () {
fireWatch("structure.format", "some-format");
}).toThrow();
});
describe("using the obtained format", function () {
var testValue = 1234321,
testText = "some text";
beforeEach(function () {
mockFormat.validate.andReturn(true);
mockFormat.parse.andReturn(testValue);
mockFormat.format.andReturn(testText);
});
it("parses user input", function () {
var newText = "some other new text";
mockScope.textValue = newText;
fireWatch("textValue", newText);
expect(mockFormat.parse).toHaveBeenCalledWith(newText);
expect(mockScope.ngModel.testField).toEqual(testValue);
});
it("validates user input", function () {
var newText = "some other new text";
mockScope.textValue = newText;
fireWatch("textValue", newText);
expect(mockFormat.validate).toHaveBeenCalledWith(newText);
});
it("formats model data for display", function () {
var newValue = 42;
mockScope.ngModel.testField = newValue;
fireWatch("ngModel[field]", newValue);
expect(mockFormat.format).toHaveBeenCalledWith(newValue);
expect(mockScope.textValue).toEqual(testText);
});
});
});
}
);

View File

@ -0,0 +1,112 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* Created by shale on 08/24/2015.
*/
define(
["../../src/controllers/ObjectInspectorController"],
function (ObjectInspectorController) {
"use strict";
describe("The object inspector controller ", function () {
var mockScope,
mockObjectService,
mockPromise,
mockDomainObject,
mockContextCapability,
mockLocationCapability,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
[ "$watch" ]
);
mockScope.ngModel = {};
mockScope.ngModel.selectedObject = 'mock selected object';
mockObjectService = jasmine.createSpyObj(
"objectService",
[ "getObjects" ]
);
mockPromise = jasmine.createSpyObj(
"promise",
[ "then" ]
);
mockObjectService.getObjects.andReturn(mockPromise);
mockDomainObject = jasmine.createSpyObj(
"selectedObject",
[ "hasCapability", "getCapability", "useCapability", "getModel" ]
);
mockDomainObject.getModel.andReturn({location: 'somewhere'});
mockDomainObject.hasCapability.andReturn(true);
mockContextCapability = jasmine.createSpyObj(
"context capability",
[ "getParent" ]
);
mockLocationCapability = jasmine.createSpyObj(
"location capability",
[ "isLink" ]
);
mockDomainObject.getCapability.andCallFake(function (param) {
if (param === 'location') {
return mockLocationCapability;
} else if (param === 'context') {
return mockContextCapability;
}
});
controller = new ObjectInspectorController(mockScope, mockObjectService);
// Change the selected object to trigger the watch call
mockScope.ngModel.selectedObject = mockDomainObject;
});
it("watches for changes to the selected object", function () {
expect(mockScope.$watch).toHaveBeenCalledWith('ngModel.selectedObject', jasmine.any(Function));
});
it("looks for contextual parent objects", function () {
mockScope.$watch.mostRecentCall.args[1]();
expect(mockContextCapability.getParent).toHaveBeenCalled();
});
it("if link, looks for primary parent objects", function () {
mockLocationCapability.isLink.andReturn(true);
mockScope.$watch.mostRecentCall.args[1]();
expect(mockDomainObject.getModel).toHaveBeenCalled();
expect(mockObjectService.getObjects).toHaveBeenCalled();
mockPromise.then.mostRecentCall.args[0]({'somewhere': mockDomainObject});
});
it("gets metadata", function () {
mockScope.$watch.mostRecentCall.args[1]();
expect(mockDomainObject.useCapability).toHaveBeenCalledWith('metadata');
});
});
}
);

View File

@ -33,7 +33,10 @@ define(
describe("The TimeRangeController", function () {
var mockScope,
mockFormatService,
testDefaultFormat,
mockNow,
mockFormat,
controller;
function fireWatch(expr, value) {
@ -57,8 +60,30 @@ define(
"$scope",
[ "$apply", "$watch", "$watchCollection" ]
);
mockFormatService = jasmine.createSpyObj(
"formatService",
[ "getFormat" ]
);
testDefaultFormat = 'utc';
mockFormat = jasmine.createSpyObj(
"format",
[ "validate", "format", "parse" ]
);
mockFormatService.getFormat.andReturn(mockFormat);
mockFormat.format.andCallFake(function (value) {
return moment.utc(value).format("YYYY-MM-DD HH:mm:ss");
});
mockNow = jasmine.createSpy('now');
controller = new TimeRangeController(mockScope, mockNow);
controller = new TimeRangeController(
mockScope,
mockFormatService,
testDefaultFormat,
mockNow
);
});
it("watches the model that was passed in", function () {
@ -167,70 +192,22 @@ define(
.toBeGreaterThan(mockScope.ngModel.inner.start);
});
describe("by typing", function () {
it("updates models", function () {
var newStart = "1977-05-25 17:30:00",
newEnd = "2015-12-18 03:30:00";
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.ngModel.outer.start)
.toEqual(moment.utc(newStart).valueOf());
expect(mockScope.boundsModel.startValid)
.toBeTruthy();
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.ngModel.outer.end)
.toEqual(moment.utc(newEnd).valueOf());
expect(mockScope.boundsModel.endValid)
.toBeTruthy();
});
it("displays error state", function () {
var newStart = "Not a date",
newEnd = "Definitely not a date",
oldStart = mockScope.ngModel.outer.start,
oldEnd = mockScope.ngModel.outer.end;
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.ngModel.outer.start)
.toEqual(oldStart);
expect(mockScope.boundsModel.startValid)
.toBeFalsy();
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.ngModel.outer.end)
.toEqual(oldEnd);
expect(mockScope.boundsModel.endValid)
.toBeFalsy();
});
it("does not modify user input", function () {
// Don't want the controller "fixing" bad or
// irregularly-formatted input out from under
// the user's fingertips.
var newStart = "Not a date",
newEnd = "2015-3-3 01:02:04",
oldStart = mockScope.ngModel.outer.start,
oldEnd = mockScope.ngModel.outer.end;
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.boundsModel.start)
.toEqual(newStart);
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.boundsModel.end)
.toEqual(newEnd);
});
});
});
it("watches for changes in format selection", function () {
expect(mockFormatService.getFormat)
.not.toHaveBeenCalledWith('test-format');
fireWatch("parameters.format", 'test-format');
expect(mockFormatService.getFormat)
.toHaveBeenCalledWith('test-format');
});
it("throws an error for unknown formats", function () {
mockFormatService.getFormat.andReturn(undefined);
expect(function () {
fireWatch("parameters.format", "some-format");
}).toThrow();
});
});
}

View File

@ -3,8 +3,10 @@
"controllers/BottomBarController",
"controllers/ClickAwayController",
"controllers/ContextMenuController",
"controllers/DateTimeFieldController",
"controllers/DateTimePickerController",
"controllers/GetterSetterController",
"controllers/ObjectInspectorController",
"controllers/SelectorController",
"controllers/SplitPaneController",
"controllers/TimeRangeController",

View File

@ -55,11 +55,6 @@ define(
self.trackPosition(event);
};
// Also make sure we dismiss bubble if representation is destroyed
// before the mouse actually leaves it
this.scopeOff =
element.scope().$on('$destroy', this.hideBubbleCallback);
this.element = element;
this.$timeout = $timeout;
this.infoService = infoService;
@ -143,7 +138,6 @@ define(
this.hideBubble();
// ...and detach listeners
this.element.off('mouseenter', this.showBubbleCallback);
this.scopeOff();
};
return InfoGesture;

View File

@ -216,18 +216,51 @@ define(
* A convenience method for info notifications. Notifications
* created via this method will be auto-dismissed after a default
* wait period
* @param {NotificationModel} notificationModel Options describing the
* notification to display
* @param {NotificationModel | string} message either a string for
* the title of the notification message, or a {@link NotificationModel}
* defining the options notification to display
* @returns {Notification} the provided notification decorated with
* functions to dismiss or minimize
*/
NotificationService.prototype.info = function (model) {
var notificationModel = typeof model === "string" ? {title: model} : model;
NotificationService.prototype.info = function (message) {
var notificationModel = typeof message === "string" ? {title: message} : message;
notificationModel.autoDismiss = notificationModel.autoDismiss || true;
notificationModel.severity = "info";
return this.notify(notificationModel);
};
/**
* A convenience method for alert notifications. Notifications
* created via this method will will have severity of "alert" enforced
* @param {NotificationModel | string} message either a string for
* the title of the alert message with default options, or a
* {@link NotificationModel} defining the options notification to
* display
* @returns {Notification} the provided notification decorated with
* functions to dismiss or minimize
*/
NotificationService.prototype.alert = function (message) {
var notificationModel = typeof message === "string" ? {title: message} : message;
notificationModel.severity = "alert";
return this.notify(notificationModel);
};
/**
* A convenience method for error notifications. Notifications
* created via this method will will have severity of "error" enforced
* @param {NotificationModel | string} message either a string for
* the title of the error message with default options, or a
* {@link NotificationModel} defining the options notification to
* display
* @returns {Notification} the provided notification decorated with
* functions to dismiss or minimize
*/
NotificationService.prototype.error = function (message) {
var notificationModel = typeof message === "string" ? {title: message} : message;
notificationModel.severity = "error";
return this.notify(notificationModel);
};
/**
* Notifies the user of an event. If there is a banner notification
* already active, then it will be dismissed or minimized automatically,

View File

@ -57,6 +57,16 @@ define(
expect(activeNotification.model).toBe(successModel);
});
it("allows specification of an info notification given just a" +
" title, making the notification active", function() {
var activeNotification,
notificationTitle = "Test info notification";
notificationService.info(notificationTitle);
activeNotification = notificationService.getActiveNotification();
expect(activeNotification.model.title).toBe(notificationTitle);
expect(activeNotification.model.severity).toBe("info");
});
it("gets a new success notification with" +
" numerical auto-dismiss specified. ", function() {
var activeNotification;

File diff suppressed because it is too large Load Diff

View File

@ -37,12 +37,14 @@ $timeControllerToiLineColorHov: #fff;
// General Colors
$colorAlt1: #ffc700;
$colorAlert: #ff533a;
$colorAlert: #ff3c00;
$colorIconLink: #49dedb;
$colorPausedBg: #c56f01;
$colorPausedFg: #fff;
$colorCreateBtn: $colorKey;
$colorGridLines: rgba(#fff, 0.05);
$colorFormLines: rgba(#fff, 0.1);
$colorFormSectionHeader: rgba(#000, 0.2);
$colorInvokeMenu: #fff;
$colorObjHdrTxt: $colorBodyFg;
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
@ -55,10 +57,10 @@ $colorMenuIc: pullForward($colorKey, 17%);
$colorMenuHovBg: pullForward($colorMenuBg, 10%);
$colorMenuHovFg: #fff;
$colorMenuHovIc: $colorMenuHovFg;
$colorCreateMenuLgIcon: $colorMenuFg;
$colorCreateMenuText: $colorMenuFg;
$shdwMenu: none;
$shdwMenuText: rgba(black, 0.1) 0 1px 2px;
$colorCreateMenuLgIcon: $colorMenuFg;
$colorCreateMenuText: $colorMenuFg;
// Form colors
$colorCheck: $colorKey;
@ -73,6 +75,14 @@ $colorInputFg: pullForward($colorBodyFg, 20%);
$colorFormText: rgba(#fff, 0.5);
$colorInputIcon: pushBack($colorBodyFg, 15%);
// Inspector
$colorInspectorBg: pullForward($colorBodyBg, 3%);
$colorInspectorFg: $colorBodyFg;
$colorInspectorPropName: pushBack($colorBodyFg, 15%);
$colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
$colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%);
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
// Status colors, mainly used for messaging and item ancillary symbols
$colorStatusFg: #ccc;
$colorStatusDefault: #ccc;
@ -139,6 +149,8 @@ $colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
// Tree
$colorItemTreeHoverBg: rgba($colorBodyFg, 0.1);
$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorItemTreeIcon: $colorKey;
$colorItemTreeIconHover: lighten($colorItemTreeIcon, 20%);
$colorItemTreeFg: $colorBodyFg;
@ -157,12 +169,13 @@ $scrollbarThumbColorHov: lighten($scrollbarThumbColor, 2%);
$scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px;
// Splitter
$splitterD: 25px; // splitterD and HandleD should both be odd, or even
$splitterHandleD: 1px;
$colorSplitterBg: pullForward($colorBodyBg, 5%);
$splitterShdw: rgba(black, 0.4) 0 0 3px;
$colorSplitterInterior: $colorBodyBg;
$colorSplitterHover: none;
$splitterW: 5px;
$splitterEndCr: 1px;
$colorGrippyInteriorHover: $colorKey;
$splitterEndCr: none;
$colorSplitterHover: pullForward($colorBodyBg, 15%);
$colorSplitterActive: $colorKey;
// Mobile
$colorMobilePaneLeft: darken($colorBodyBg, 5%);
@ -179,4 +192,4 @@ $colorAboutLink: #84b3ff;
// Loading
$colorLoadingBg: rgba($colorBodyFg, 0.2);
$colorLoadingFg: $colorAlt1;
$colorLoadingFg: $colorAlt1;

View File

@ -4,7 +4,7 @@
@include boxShdw($shdwBtns);
}
@mixin btnSubtle($bg: $colorBodyBg, $bgHov: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
@mixin btnSubtle($bg: $colorBodyBg, $bgHov: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
@include containerSubtle($bg, $fg);
@include btnBase($bg, linear-gradient(lighten($bg, 15%), lighten($bg, 10%)), $fg, $ic);
@include text-shadow($shdwItemText);

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,14 @@ $colorInputIcon: pushBack($colorBodyFg, 25%);
$colorSelectBg: #ddd;
$colorSelectFg: $colorBodyFg;
// Inspector
$colorInspectorBg: pullForward($colorBodyBg, 5%);
$colorInspectorFg: $colorBodyFg;
$colorInspectorPropName: pushBack($colorBodyFg, 20%);
$colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
$colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%);
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
// Limits and staleness colors//
$colorTelemFresh: pullForward($colorBodyFg, 20%);
$colorTelemStale: pushBack($colorBodyFg, 20%);
@ -110,6 +118,7 @@ $colorItemBgSelected: $colorKey;
// Tabular
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: pullForward($colorBodyBg, 10%);
@ -125,6 +134,7 @@ $colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
// Tree
$colorItemTreeHoverBg: rgba($colorBodyFg, 0.1);
$colorItemTreeIcon: $colorKey;
$colorItemTreeIconHover: $colorItemTreeIcon; //pushBack($colorItemTreeIcon, 20%);
$colorItemTreeVCHover: $colorKey;
@ -143,12 +153,13 @@ $scrollbarThumbColor: darken($colorBodyBg, 50%);//
$scrollbarThumbColorHov: $colorKey;
// Splitter
$splitterD: 24px;
$splitterHandleD: 2px;
$colorSplitterBg: pullForward($colorBodyBg, 10%);
$splitterShdw: none;
$colorSplitterInterior: $colorBodyBg;
$colorSplitterHover: $colorKey;
$splitterW: 5px;
$splitterEndCr: $splitterW;
$colorGrippyInteriorHover: $colorBodyBg;
$splitterEndCr: none;
$colorSplitterHover: none;
$colorSplitterActive: $colorKey;
// Mobile
$colorMobilePaneLeft: darken($colorBodyBg, 2%);

View File

@ -101,13 +101,27 @@ define(
self.notificationService.info("Copying complete.");
},
function error(errorDetails){
self.$log.error("Error copying objects. ", errorDetails);
//Show more general error message
self.notificationService.notify({
var errorMessage = {
title: "Error copying objects.",
severity: "error",
hint: errorDetails.message
});
hint: errorDetails.message,
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function() {
self.dialogService.dismiss();
}
}]
};
self.dialogService.dismiss();
if (self.notification) {
self.notification.dismiss(); // Clear the progress notification
}
self.$log.error("Error copying objects. ", errorDetails);
//Show more general error message
self.notificationService.notify(errorMessage);
self.dialogService.showBlockingMessage(errorMessage);
},
function notification(details){

View File

@ -200,7 +200,7 @@ define(
this.buildCopyPlan(domainObject, parent, deferred.notify)
.then(this.persistObjects(deferred.notify))
.then(this.addClonesToParent(parent, deferred.notify))
.then(deferred.resolve);
.then(deferred.resolve, deferred.reject);
return deferred.promise;
} else {
throw new Error(

View File

@ -36,9 +36,9 @@
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{ "key": "time", "name": "Time" },
{ "key": "yesterday", "name": "Yesterday" }
{ "key": "time", "name": "UTC", "format": "utc" }
],
"priority": "fallback",
"comment": "Placeholder; to be replaced by inspection of available domains."
}
]

View File

@ -1,4 +1,5 @@
<mct-include key="'time-controller'"
parameters='parameters'
ng-model='ngModel.conductor'>
</mct-include>
<mct-control key="'select'"

View File

@ -27,7 +27,10 @@ define(
"use strict";
var TEMPLATE = [
"<mct-include key=\"'time-conductor'\" ng-model='ngModel' class='l-time-controller'>",
"<mct-include key=\"'time-conductor'\" ",
"ng-model='ngModel' ",
"parameters='parameters' ",
"class='l-time-controller'>",
"</mct-include>"
].join(''),
THROTTLE_MS = 200,
@ -74,11 +77,11 @@ define(
broadcastBounds;
// Combine start/end times into a single object
function bounds(start, end) {
function bounds() {
return {
start: conductor.displayStart(),
end: conductor.displayEnd(),
domain: conductor.domain()
domain: conductor.domain().key
};
}
@ -97,12 +100,9 @@ define(
}
function updateDomain(value) {
conductor.domain(value);
repScope.$broadcast('telemetry:display:bounds', bounds(
conductor.displayStart(),
conductor.displayEnd(),
conductor.domain()
));
var newDomain = conductor.domain(value);
conductorScope.parameters.format = newDomain.format;
repScope.$broadcast('telemetry:display:bounds', bounds());
}
// telemetry domain metadata -> option for a select control
@ -130,7 +130,8 @@ define(
{ outer: bounds(), inner: bounds() };
conductorScope.ngModel.options =
conductor.domainOptions().map(makeOption);
conductorScope.ngModel.domain = conductor.domain();
conductorScope.ngModel.domain = conductor.domain().key;
conductorScope.parameters = {};
conductorScope
.$watch('ngModel.conductor.inner.start', updateConductorInner);

View File

@ -51,7 +51,7 @@ define(
request = request || {};
request.start = start;
request.end = end;
request.domain = domain;
request.domain = domain.key;
return request;
}

View File

@ -43,7 +43,7 @@ define(
function TimeConductor(start, end, domains) {
this.range = { start: start, end: end };
this.domains = domains;
this.activeDomain = domains[0].key;
this.activeDomain = domains[0];
}
/**
@ -73,7 +73,7 @@ define(
/**
* Get available domain options which can be used to bound time
* selection.
* @returns {TelemetryDomain[]} available domains
* @returns {TelemetryDomainMetadata[]} available domains
*/
TimeConductor.prototype.domainOptions = function () {
return this.domains;
@ -82,19 +82,21 @@ define(
/**
* Get or set (if called with an argument) the active domain.
* @param {string} [key] the key identifying the domain choice
* @returns {TelemetryDomain} the active telemetry domain
* @returns {TelemetryDomainMetadata} the active telemetry domain
*/
TimeConductor.prototype.domain = function (key) {
function matchesKey(domain) {
return domain.key === key;
}
var i;
if (arguments.length > 0) {
if (!this.domains.some(matchesKey)) {
throw new Error("Unknown domain " + key);
for (i = 0; i < this.domains.length; i += 1) {
if (this.domains[i].key === key) {
return (this.activeDomain = this.domains[i]);
}
}
this.activeDomain = key;
throw new Error("Unknown domain " + key);
}
return this.activeDomain;
};

View File

@ -129,7 +129,7 @@ define(
it("exposes conductor state in scope", function () {
mockConductor.displayStart.andReturn(1977);
mockConductor.displayEnd.andReturn(1984);
mockConductor.domain.andReturn('d');
mockConductor.domain.andReturn({ key: 'd' });
representer.represent(testViews[0], {});
expect(mockNewScope.ngModel.conductor).toEqual({
@ -219,7 +219,7 @@ define(
representer.represent(testViews[0], null);
expect(mockNewScope.ngModel.domain)
.toEqual(mockConductor.domain());
.toEqual(mockConductor.domain().key);
});
it("exposes domain options in scope", function () {

View File

@ -77,7 +77,7 @@ define(
mockConductor.displayStart.andReturn(42);
mockConductor.displayEnd.andReturn(1977);
mockConductor.domain.andReturn("testDomain");
mockConductor.domain.andReturn({ key: "testDomain" });
decorator = new ConductorTelemetryDecorator(
mockConductorService,
@ -104,7 +104,7 @@ define(
});
it("with domain selection", function () {
expect(request.domain).toEqual(mockConductor.domain());
expect(request.domain).toEqual(mockConductor.domain().key);
});
});
@ -127,7 +127,7 @@ define(
});
it("with domain selection", function () {
expect(request.domain).toEqual(mockConductor.domain());
expect(request.domain).toEqual(mockConductor.domain().key);
});
});

View File

@ -59,12 +59,12 @@ define(
});
it("exposes the current domain choice", function () {
expect(conductor.domain()).toEqual(testDomains[0].key);
expect(conductor.domain()).toEqual(testDomains[0]);
});
it("allows the domain choice to be changed", function () {
conductor.domain(testDomains[1].key);
expect(conductor.domain()).toEqual(testDomains[1].key);
expect(conductor.domain()).toEqual(testDomains[1]);
});
it("throws an error on attempts to set an invalid domain", function () {

View File

@ -31,10 +31,19 @@ define(
"./elements/PlotPalette",
"./elements/PlotAxis",
"./elements/PlotLimitTracker",
"./elements/PlotTelemetryFormatter",
"./modes/PlotModeOptions",
"./SubPlotFactory"
],
function (PlotUpdater, PlotPalette, PlotAxis, PlotLimitTracker, PlotModeOptions, SubPlotFactory) {
function (
PlotUpdater,
PlotPalette,
PlotAxis,
PlotLimitTracker,
PlotTelemetryFormatter,
PlotModeOptions,
SubPlotFactory
) {
"use strict";
var AXIS_DEFAULTS = [
@ -62,7 +71,10 @@ define(
PLOT_FIXED_DURATION
) {
var self = this,
subPlotFactory = new SubPlotFactory(telemetryFormatter),
plotTelemetryFormatter =
new PlotTelemetryFormatter(telemetryFormatter),
subPlotFactory =
new SubPlotFactory(plotTelemetryFormatter),
cachedObjects = [],
updater,
lastBounds,
@ -71,10 +83,9 @@ define(
// Populate the scope with axis information (specifically, options
// available for each axis.)
function setupAxes(metadatas) {
$scope.axes = [
new PlotAxis("domain", metadatas, AXIS_DEFAULTS[0]),
new PlotAxis("range", metadatas, AXIS_DEFAULTS[1])
];
$scope.axes.forEach(function (axis) {
axis.updateMetadata(metadatas);
});
}
// Trigger an update of a specific subplot;
@ -125,37 +136,49 @@ define(
}
}
function getUpdater() {
if (!updater) {
recreateUpdater();
}
return updater;
}
// Handle new telemetry data in this plot
function updateValues() {
self.pending = false;
if (handle) {
setupModes(handle.getTelemetryObjects());
}
if (updater) {
updater.update();
setupAxes(handle.getMetadata());
getUpdater().update();
self.modeOptions.getModeHandler().plotTelemetry(updater);
}
if (self.limitTracker) {
self.limitTracker.update();
self.update();
}
self.update();
}
// Display new historical data as it becomes available
function addHistoricalData(domainObject, series) {
self.pending = false;
updater.addHistorical(domainObject, series);
getUpdater().addHistorical(domainObject, series);
self.modeOptions.getModeHandler().plotTelemetry(updater);
self.update();
}
// Issue a new request for historical telemetry
function requestTelemetry() {
if (handle && updater) {
if (handle) {
handle.request({}, addHistoricalData);
}
}
// Requery for data entirely
function replot() {
if (handle) {
updater = undefined;
requestTelemetry();
}
}
// Create a new subscription; telemetrySubscriber gets
// to do the meaningful work here.
function subscribe(domainObject) {
@ -167,12 +190,7 @@ define(
updateValues,
true // Lossless
);
if (handle) {
setupModes(handle.getTelemetryObjects());
setupAxes(handle.getMetadata());
recreateUpdater();
requestTelemetry();
}
replot();
}
// Release the current subscription (called when scope is destroyed)
@ -185,12 +203,22 @@ define(
// Respond to a display bounds change (requery for data)
function changeDisplayBounds(event, bounds) {
var domainAxis = $scope.axes[0];
domainAxis.chooseOption(bounds.domain);
plotTelemetryFormatter
.setDomainFormat(domainAxis.active.format);
self.pending = true;
releaseSubscription();
subscribe($scope.domainObject);
setBasePanZoom(bounds);
}
function updateDomainFormat(format) {
plotTelemetryFormatter.setDomainFormat(format);
}
this.modeOptions = new PlotModeOptions([], subPlotFactory);
this.updateValues = updateValues;
@ -202,6 +230,13 @@ define(
self.pending = true;
// Initialize axes; will get repopulated when telemetry
// metadata becomes available.
$scope.axes = [
new PlotAxis("domains", [], AXIS_DEFAULTS[0]),
new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
];
// Subscribe to telemetry when a domain object becomes available
$scope.$watch('domainObject', subscribe);

View File

@ -121,9 +121,9 @@ define(
// Utility, for map/forEach loops. Index 0 is domain,
// index 1 is range.
function formatValue(v, i) {
return (i ?
formatter.formatRangeValue :
formatter.formatDomainValue)(v);
return i ?
formatter.formatRangeValue(v) :
formatter.formatDomainValue(v);
}
this.hoverCoordinates = this.mousePosition &&

View File

@ -46,21 +46,9 @@ define(
*
*/
function PlotAxis(axisType, metadatas, defaultValue) {
var keys = {},
options = [];
// Look through all metadata objects and assemble a list
// of all possible domain or range options
function buildOptionsForMetadata(m) {
(m[axisType] || []).forEach(function (option) {
if (!keys[option.key]) {
keys[option.key] = true;
options.push(option);
}
});
}
(metadatas || []).forEach(buildOptionsForMetadata);
this.axisType = axisType;
this.defaultValue = defaultValue;
this.optionKeys = {};
/**
* The currently chosen option for this axis. An
@ -68,7 +56,7 @@ define(
* directly form the plot template.
* @memberof platform/features/plot.PlotAxis#
*/
this.active = options[0] || defaultValue;
this.active = defaultValue;
/**
* The set of options applicable for this axis;
@ -77,9 +65,71 @@ define(
* human-readable names respectively)
* @memberof platform/features/plot.PlotAxis#
*/
this.options = options;
this.options = [];
// Initialize options from metadata objects
this.updateMetadata(metadatas);
}
/**
* Update axis options to reflect current metadata.
* @param {TelemetryMetadata[]} metadata objects describing
* applicable telemetry
*/
PlotAxis.prototype.updateMetadata = function (metadatas) {
var axisType = this.axisType,
optionKeys = this.optionKeys,
newOptions = {},
toAdd = [];
function isValid(option) {
return option && optionKeys[option.key];
}
metadatas.forEach(function (m) {
(m[axisType] || []).forEach(function (option) {
var key = option.key;
if (!optionKeys[key] && !newOptions[key]) {
toAdd.push(option);
}
newOptions[key] = true;
});
});
optionKeys = this.optionKeys = newOptions;
// General approach here is to avoid changing object
// instances unless something has really changed, since
// Angular is watching; don't want to trigger extra digests.
if (!this.options.every(isValid)) {
this.options = this.options.filter(isValid);
}
if (toAdd.length > 0) {
this.options = this.options.concat(toAdd);
}
if (!isValid(this.active)) {
this.active = this.options[0] || this.defaultValue;
}
};
/**
* Change the domain/range selection for this axis. If the
* provided `key` is not recognized as an option, no change
* will occur.
* @param {string} key the identifier for the domain/range
*/
PlotAxis.prototype.chooseOption = function (key) {
var self = this;
this.options.forEach(function (option) {
if (option.key === key) {
self.active = option;
}
});
};
return PlotAxis;
}

View File

@ -0,0 +1,72 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
[],
function () {
'use strict';
/**
* Wraps a `TelemetryFormatter` to provide formats for domain and
* range values; provides a single place to track domain/range
* formats within a plot, allowing other plot elements to simply
* request that values be formatted.
* @constructor
* @memberof platform/features/plot
* @implements {platform/telemetry.TelemetryFormatter}
* @param {TelemetryFormatter} telemetryFormatter the formatter
* to wrap.
*/
function PlotTelemetryFormatter(telemetryFormatter) {
this.telemetryFormatter = telemetryFormatter;
}
/**
* Specify the format to use for domain values.
* @param {string} key the format's identifier
*/
PlotTelemetryFormatter.prototype.setDomainFormat = function (key) {
this.domainFormat = key;
};
/**
* Specify the format to use for range values.
* @param {string} key the format's identifier
*/
PlotTelemetryFormatter.prototype.setRangeFormat = function (key) {
this.rangeFormat = key;
};
PlotTelemetryFormatter.prototype.formatDomainValue = function (value) {
return this.telemetryFormatter
.formatDomainValue(value, this.domainFormat);
};
PlotTelemetryFormatter.prototype.formatRangeValue = function (value) {
return this.telemetryFormatter
.formatRangeValue(value, this.rangeFormat);
};
return PlotTelemetryFormatter;
}
);

View File

@ -43,6 +43,14 @@ define(
this.formatter = formatter;
}
// For phantomjs compatibility, for headless testing
// (Function.prototype.bind unsupported)
function bind(fn, thisObj) {
return fn.bind ? fn.bind(thisObj) : function () {
return fn.apply(thisObj, arguments);
};
}
// Generate ticks; interpolate from start up to
// start + span in count steps, using the provided
// formatter to represent each value.
@ -72,7 +80,7 @@ define(
panZoom.origin[0],
panZoom.dimensions[0],
count,
this.formatter.formatDomainValue
bind(this.formatter.formatDomainValue, this.formatter)
);
};
@ -87,7 +95,7 @@ define(
panZoom.origin[1],
panZoom.dimensions[1],
count,
this.formatter.formatRangeValue
bind(this.formatter.formatRangeValue, this.formatter)
);
};

View File

@ -169,8 +169,9 @@ define(
mockDomainObject
]);
// Make an object available
// Make an object available; invoke handler's callback
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
mockHandler.handle.mostRecentCall.args[1]();
expect(controller.getModeOptions().length).toEqual(1);
@ -181,8 +182,9 @@ define(
mockDomainObject
]);
// Make an object available
// Make an object available; invoke handler's callback
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
mockHandler.handle.mostRecentCall.args[1]();
expect(controller.getModeOptions().length).toEqual(2);
});

View File

@ -30,7 +30,12 @@ define(
"use strict";
describe("A plot axis", function () {
var testMetadatas = [
var testMetadatas,
testDefault,
axis;
beforeEach(function () {
testMetadatas = [
{
tests: [
{ key: "t0", name: "T0" },
@ -52,13 +57,14 @@ define(
{ key: "t6", name: "T6" }
]
}
],
testDefault = { key: "test", name: "Test" },
controller = new PlotAxis("tests", testMetadatas, testDefault);
];
testDefault = { key: "test", name: "Test" };
axis = new PlotAxis("tests", testMetadatas, testDefault);
});
it("pulls out a list of domain or range options", function () {
// Should have filtered out duplicates, etc
expect(controller.options).toEqual([
expect(axis.options).toEqual([
{ key: "t0", name: "T0" },
{ key: "t1", name: "T1" },
{ key: "t2", name: "T2" },
@ -70,7 +76,7 @@ define(
});
it("chooses the first option as a default", function () {
expect(controller.active).toEqual({ key: "t0", name: "T0" });
expect(axis.active).toEqual({ key: "t0", name: "T0" });
});
it("falls back to a provided default if no options are present", function () {
@ -78,6 +84,26 @@ define(
.toEqual(testDefault);
});
it("allows options to be chosen by key", function () {
axis.chooseOption("t3");
expect(axis.active).toEqual({ key: "t3", name: "T3" });
});
it("reflects changes to applicable metadata", function () {
axis.updateMetadata([ testMetadatas[1] ]);
expect(axis.options).toEqual([
{ key: "t0", name: "T0" },
{ key: "t2", name: "T2" }
]);
});
it("returns the same array instance for unchanged metadata", function () {
// ...to avoid triggering extra digest cycles.
var oldInstance = axis.options;
axis.updateMetadata(testMetadatas);
expect(axis.options).toBe(oldInstance);
});
});
}
);
);

View File

@ -0,0 +1,70 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../../src/elements/PlotTelemetryFormatter"],
function (PlotTelemetryFormatter) {
'use strict';
describe("The PlotTelemetryFormatter", function () {
var mockFormatter,
formatter;
beforeEach(function () {
mockFormatter = jasmine.createSpyObj(
'telemetryFormatter',
['formatDomainValue', 'formatRangeValue']
);
formatter = new PlotTelemetryFormatter(mockFormatter);
});
describe("using domain & range format keys", function () {
var rangeFormat = "someRangeFormat",
domainFormat = "someDomainFormat";
beforeEach(function () {
formatter.setRangeFormat(rangeFormat);
formatter.setDomainFormat(domainFormat);
});
it("includes format in formatDomainValue calls", function () {
mockFormatter.formatDomainValue.andReturn("formatted!");
expect(formatter.formatDomainValue(12321))
.toEqual("formatted!");
expect(mockFormatter.formatDomainValue)
.toHaveBeenCalledWith(12321, domainFormat);
});
it("includes format in formatRangeValue calls", function () {
mockFormatter.formatRangeValue.andReturn("formatted!");
expect(formatter.formatRangeValue(12321))
.toEqual("formatted!");
expect(mockFormatter.formatRangeValue)
.toHaveBeenCalledWith(12321, rangeFormat);
});
});
});
}
);

View File

@ -14,6 +14,7 @@
"elements/PlotPosition",
"elements/PlotPreparer",
"elements/PlotSeriesWindow",
"elements/PlotTelemetryFormatter",
"elements/PlotTickGenerator",
"elements/PlotUpdater",
"modes/PlotModeOptions",

View File

@ -54,7 +54,8 @@ define(
DomainColumn.prototype.getValue = function (domainObject, datum) {
return {
text: this.telemetryFormatter.formatDomainValue(
datum[this.domainMetadata.key]
datum[this.domainMetadata.key],
this.domainMetadata.format
)
};
};

View File

@ -4,12 +4,12 @@
{
"key": "mctInclude",
"implementation": "MCTInclude.js",
"depends": [ "templates[]", "$sce" ]
"depends": [ "templates[]", "templateLinker" ]
},
{
"key": "mctRepresentation",
"implementation": "MCTRepresentation.js",
"depends": [ "representations[]", "views[]", "representers[]", "$q", "$sce", "$log" ]
"depends": [ "representations[]", "views[]", "representers[]", "$q", "templateLinker", "$log" ]
}
],
"gestures": [
@ -48,6 +48,12 @@
"key": "dndService",
"implementation": "services/DndService.js",
"depends": [ "$log" ]
},
{
"key": "templateLinker",
"implementation": "TemplateLinker.js",
"depends": [ "$templateRequest", "$sce", "$compile", "$log" ],
"comment": "For internal use by mct-include and mct-representation."
}
],
"actions": [

View File

@ -54,36 +54,38 @@ define(
* @param {TemplateDefinition[]} templates an array of
* template extensions
*/
function MCTInclude(templates, $sce) {
function MCTInclude(templates, templateLinker) {
var templateMap = {};
function link(scope, element) {
var changeTemplate = templateLinker.link(
scope,
element,
scope.key && templateMap[scope.key]
);
scope.$watch('key', function (key) {
changeTemplate(key && templateMap[key]);
});
}
// Prepopulate templateMap for easy look up by key
templates.forEach(function (template) {
var key = template.key,
path = $sce.trustAsResourceUrl([
template.bundle.path,
template.bundle.resources,
template.templateUrl
].join("/"));
var key = template.key;
// First found should win (priority ordering)
templateMap[key] = templateMap[key] || path;
templateMap[key] =
templateMap[key] || templateLinker.getPath(template);
});
function controller($scope) {
// Pass the template URL to ng-include via scope.
$scope.inclusion = templateMap[$scope.key];
}
return {
// Only show at the element level
restrict: "E",
// Use the included controller to populate scope
controller: controller,
link: link,
// Use ng-include as a template; "inclusion" will be the real
// template path
template: '<ng-include src="inclusion"></ng-include>',
// May hide the element, so let other directives act first
priority: -1000,
// Two-way bind key, ngModel, and parameters
scope: { key: "=", ngModel: "=", parameters: "=" }

View File

@ -31,7 +31,6 @@ define(
function () {
"use strict";
/**
* Defines the mct-representation directive. This may be used to
* present domain objects as HTML (with event wiring), with the
@ -55,7 +54,7 @@ define(
* representation extensions
* @param {ViewDefinition[]} views an array of view extensions
*/
function MCTRepresentation(representations, views, representers, $q, $sce, $log) {
function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
var representationMap = {},
gestureMap = {};
@ -72,11 +71,7 @@ define(
// Get a path to a representation
function getPath(representation) {
return $sce.trustAsResourceUrl([
representation.bundle.path,
representation.bundle.resources,
representation.templateUrl
].join("/"));
return templateLinker.getPath(representation);
}
// Look up a matching representation for this domain object
@ -94,12 +89,16 @@ define(
}
}
function link($scope, element, attrs) {
function link($scope, element, attrs, ctrl, transclude) {
var activeRepresenters = representers.map(function (Representer) {
return new Representer($scope, element, attrs);
}),
toClear = [], // Properties to clear out of scope on change
counter = 0;
counter = 0,
couldRepresent = false,
lastId,
lastKey,
changeTemplate = templateLinker.link($scope, element);
// Populate scope with any capabilities indicated by the
// representation's extension definition
@ -144,21 +143,36 @@ define(
});
}
function unchanged(canRepresent, id, key) {
return canRepresent &&
couldRepresent &&
id === lastId &&
key === lastKey;
}
// General-purpose refresh mechanism; should set up the scope
// as appropriate for current representation key and
// domain object.
function refresh() {
var domainObject = $scope.domainObject,
representation = lookup($scope.key, domainObject),
uses = ((representation || {}).uses || []);
path = representation && getPath(representation),
uses = ((representation || {}).uses || []),
canRepresent = !!(path && domainObject),
id = domainObject && domainObject.getId(),
key = $scope.key;
if (unchanged(canRepresent, id, key)) {
return;
}
// Create an empty object named "representation", for this
// representation to store local variables into.
$scope.representation = {};
// Look up the actual template path, pass it to ng-include
// via the "inclusion" field
$scope.inclusion = representation && getPath(representation);
// Change templates (passing in undefined to clear
// if we don't have enough info to show a template.)
changeTemplate(canRepresent ? path : undefined);
// Any existing representers are no longer valid; release them.
destroyRepresenters();
@ -174,9 +188,14 @@ define(
delete $scope[property];
});
// To allow simplified change detection next time around
couldRepresent = canRepresent;
lastId = id;
lastKey = key;
// Populate scope with fields associated with the current
// domain object (if one has been passed in)
if (domainObject) {
if (canRepresent) {
// Track how many representations we've made in this scope,
// to ensure that the correct representations are matched to
// the correct object/key pairs.
@ -233,9 +252,8 @@ define(
// Handle Angular's linking step
link: link,
// Use ng-include as a template; "inclusion" will be the real
// template path
template: '<ng-include src="inclusion"></ng-include>',
// May hide the element, so let other directives act first
priority: -1000,
// Two-way bind key and parameters, get the represented domain
// object as "mct-object"

View File

@ -0,0 +1,163 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
define(
[],
function () {
"use strict";
/**
* The `templateLinker` service is intended for internal use by
* the `mct-include` and `mct-representation` directives. It is
* used to support common behavior of directives; specifically,
* loading templates and inserting them into a specified element,
* and/or removing that element from the DOM when there is no
* template to populate it with.
*
* @param {Function} $templateRequest Angular's `$templateRequest`
* service
* @param $sce Angular's `$sce` service
* @param {Function} $compile Angular's `$compile` service
* @param $log Angular's `$log` service
* @private
*/
function TemplateLinker($templateRequest, $sce, $compile, $log) {
this.$templateRequest = $templateRequest;
this.$sce = $sce;
this.$compile = $compile;
this.$log = $log;
}
/**
* Load a template from the given URL. This request will be handled
* via `$templateRequest` to ensure caching et cetera.
* @param {string} the URL for the template
* @returns {Promise.<string>} a promise for the HTML content of
* the template
* @private
*/
TemplateLinker.prototype.load = function (templateUrl) {
return this.$templateRequest(
this.$sce.trustAsResourceUrl(templateUrl),
false
);
};
/**
* Get a path to a template from an extension definition fo
* a template, representation, or view.
* @param {TemplateDefinition} extensionDefinition the definition
* of the template/representation/view to resolve
*/
TemplateLinker.prototype.getPath = function (extensionDefinition) {
return [
extensionDefinition.bundle.path,
extensionDefinition.bundle.resources,
extensionDefinition.templateUrl
].join('/');
};
/**
* Populate the given element with templates, within the given scope;
* intended to support the `link` function of the supported directives.
*
* @param {Scope} scope the Angular scope to use when rendering
* templates
* @param element the jqLite-wrapped element into which templates
* should be inserted
* @returns {Function} a function which can be called with a template
* URL to switch templates, or `undefined` to remove.
*/
TemplateLinker.prototype.link = function (scope, element, templateUrl) {
var activeElement = element,
activeTemplateUrl,
comment = this.$compile('<!-- hidden mct element -->')(scope),
activeScope,
self = this;
function destroyScope() {
if (activeScope) {
activeScope.$destroy();
activeScope = undefined;
}
}
function removeElement() {
if (activeElement !== comment) {
destroyScope();
activeElement.replaceWith(comment);
activeElement = comment;
}
}
function addElement() {
if (activeElement !== element) {
activeElement.replaceWith(element);
activeElement = element;
activeElement.empty();
}
}
function populateElement(template) {
destroyScope();
activeScope = scope.$new(false);
element.html(template);
self.$compile(element.contents())(activeScope);
}
function badTemplate(templateUrl) {
self.$log.warn("Couldn't load template at " + templateUrl);
removeElement();
}
function changeTemplate(templateUrl) {
if (templateUrl) {
destroyScope();
addElement();
self.load(templateUrl).then(function (template) {
// Avoid race conditions
if (templateUrl === activeTemplateUrl) {
populateElement(template);
}
}, function () {
badTemplate(templateUrl);
});
} else {
removeElement();
}
activeTemplateUrl = templateUrl;
}
if (templateUrl) {
changeTemplate(templateUrl);
} else {
removeElement();
}
return changeTemplate;
};
return TemplateLinker;
}
);

View File

@ -31,9 +31,21 @@ define(
describe("The mct-include directive", function () {
var testTemplates,
mockSce,
testUrls,
mockLinker,
mockScope,
mockElement,
mockChangeTemplate,
mctInclude;
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1](value);
}
});
}
beforeEach(function () {
testTemplates = [
{
@ -47,40 +59,44 @@ define(
templateUrl: "z/template.html"
}
];
mockSce = jasmine.createSpyObj(
'$sce',
['trustAsResourceUrl']
);
mockSce.trustAsResourceUrl.andCallFake(function (url) {
return url;
testUrls = {};
testTemplates.forEach(function (t, i) {
testUrls[t.key] = "some URL " + String(i);
});
mctInclude = new MCTInclude(testTemplates, mockSce);
});
it("has a built-in template, with ng-include src=inclusion", function () {
// Not rigorous, but should detect many cases when template is broken.
expect(mctInclude.template.indexOf("ng-include")).not.toEqual(-1);
expect(mctInclude.template.indexOf("inclusion")).not.toEqual(-1);
mockLinker = jasmine.createSpyObj(
'templateLinker',
['link', 'getPath']
);
mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
mockElement = jasmine.createSpyObj('element', ['empty']);
mockChangeTemplate = jasmine.createSpy('changeTemplate');
mockLinker.link.andReturn(mockChangeTemplate);
mockLinker.getPath.andCallFake(function (template) {
return testUrls[template.key];
});
mctInclude = new MCTInclude(testTemplates, mockLinker);
mctInclude.link(mockScope, mockElement, {});
});
it("is restricted to elements", function () {
expect(mctInclude.restrict).toEqual("E");
});
it("reads a template location from a scope's key variable", function () {
var scope = { key: "abc" };
mctInclude.controller(scope);
expect(scope.inclusion).toEqual("a/b/c/template.html");
scope = { key: "xyz" };
mctInclude.controller(scope);
expect(scope.inclusion).toEqual("x/y/z/template.html");
it("exposes templates via the templateLinker", function () {
expect(mockLinker.link)
.toHaveBeenCalledWith(mockScope, mockElement, undefined);
});
it("trusts template URLs", function () {
mctInclude.controller({ key: "xyz" });
expect(mockSce.trustAsResourceUrl)
.toHaveBeenCalledWith("x/y/z/template.html");
it("reads a template location from a scope's key variable", function () {
mockScope.key = 'abc';
fireWatch('key', mockScope.key);
expect(mockChangeTemplate)
.toHaveBeenCalledWith(testUrls.abc);
mockScope.key = 'xyz';
fireWatch('key', mockScope.key);
expect(mockChangeTemplate)
.toHaveBeenCalledWith(testUrls.xyz);
});
});

View File

@ -36,10 +36,12 @@ define(
describe("The mct-representation directive", function () {
var testRepresentations,
testViews,
testUrls,
mockRepresenters,
mockQ,
mockSce,
mockLinker,
mockLog,
mockChangeTemplate,
mockScope,
mockElement,
mockDomainObject,
@ -54,7 +56,17 @@ define(
};
}
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1](value);
}
});
}
beforeEach(function () {
testUrls = {};
testRepresentations = [
{
key: "abc",
@ -85,6 +97,11 @@ define(
testModel = { someKey: "some value" };
testUrls = {};
testViews.concat(testRepresentations).forEach(function (t, i) {
testUrls[t.key] = "some URL " + String(i);
});
mockRepresenters = ["A", "B"].map(function (name) {
var constructor = jasmine.createSpy("Representer" + name),
representer = jasmine.createSpyObj(
@ -96,45 +113,44 @@ define(
});
mockQ = { when: mockPromise };
mockSce = jasmine.createSpyObj(
'$sce',
['trustAsResourceUrl']
mockLinker = jasmine.createSpyObj(
'templateLinker',
['link', 'getPath']
);
mockChangeTemplate = jasmine.createSpy('changeTemplate');
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
mockSce.trustAsResourceUrl.andCallFake(function (url) {
return url;
});
mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
mockDomainObject.getModel.andReturn(testModel);
mockLinker.link.andReturn(mockChangeTemplate);
mockLinker.getPath.andCallFake(function (ext) {
return testUrls[ext.key];
});
mctRepresentation = new MCTRepresentation(
testRepresentations,
testViews,
mockRepresenters,
mockQ,
mockSce,
mockLinker,
mockLog
);
});
it("has a built-in template, with ng-include src=inclusion", function () {
// Not rigorous, but should detect many cases when template is broken.
expect(mctRepresentation.template.indexOf("ng-include")).not.toEqual(-1);
expect(mctRepresentation.template.indexOf("inclusion")).not.toEqual(-1);
mctRepresentation.link(mockScope, mockElement);
});
it("is restricted to elements", function () {
expect(mctRepresentation.restrict).toEqual("E");
});
it("exposes templates via the templateLinker", function () {
expect(mockLinker.link)
.toHaveBeenCalledWith(mockScope, mockElement);
});
it("watches scope when linked", function () {
mctRepresentation.link(mockScope, mockElement);
expect(mockScope.$watch).toHaveBeenCalledWith(
"key",
jasmine.any(Function)
@ -150,42 +166,46 @@ define(
});
it("recognizes keys for representations", function () {
mctRepresentation.link(mockScope, mockElement);
mockScope.key = "abc";
mockScope.domainObject = mockDomainObject;
// Trigger the watch
mockScope.$watch.calls[0].args[1]();
fireWatch('key', mockScope.key);
fireWatch('domainObject', mockDomainObject);
expect(mockScope.inclusion).toEqual("a/b/c/template.html");
expect(mockChangeTemplate)
.toHaveBeenCalledWith(testUrls.abc);
});
it("recognizes keys for views", function () {
mctRepresentation.link(mockScope, mockElement);
mockScope.key = "xyz";
mockScope.domainObject = mockDomainObject;
// Trigger the watch
mockScope.$watch.calls[0].args[1]();
// Trigger the watches
fireWatch('key', mockScope.key);
fireWatch('domainObject', mockDomainObject);
expect(mockScope.inclusion).toEqual("x/y/z/template.html");
expect(mockChangeTemplate)
.toHaveBeenCalledWith(testUrls.xyz);
});
it("trusts template URLs", function () {
mctRepresentation.link(mockScope, mockElement);
it("does not load templates until there is an object", function () {
mockScope.key = "xyz";
// Trigger the watch
mockScope.$watch.calls[0].args[1]();
fireWatch('key', mockScope.key);
expect(mockSce.trustAsResourceUrl)
.toHaveBeenCalledWith("x/y/z/template.html");
expect(mockChangeTemplate)
.not.toHaveBeenCalledWith(jasmine.any(String));
mockScope.domainObject = mockDomainObject;
fireWatch('domainObject', mockDomainObject);
expect(mockChangeTemplate)
.toHaveBeenCalledWith(jasmine.any(String));
});
it("loads declared capabilities", function () {
mctRepresentation.link(mockScope, mockElement);
mockScope.key = "def";
mockScope.domainObject = mockDomainObject;
@ -199,8 +219,6 @@ define(
});
it("logs when no representation is available for a key", function () {
mctRepresentation.link(mockScope, mockElement);
mockScope.key = "someUnknownThing";
// Verify precondition
@ -214,8 +232,6 @@ define(
});
it("clears out obsolete peroperties from scope", function () {
mctRepresentation.link(mockScope, mockElement);
mockScope.key = "def";
mockScope.domainObject = mockDomainObject;
mockDomainObject.useCapability.andReturn("some value");

View File

@ -0,0 +1,230 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/TemplateLinker"],
function (TemplateLinker) {
'use strict';
var JQLITE_METHODS = [ 'replaceWith', 'empty', 'html', 'contents' ],
SCOPE_METHODS = [ '$on', '$new', '$destroy' ];
describe("TemplateLinker", function () {
var mockTemplateRequest,
mockSce,
mockCompile,
mockLog,
mockScope,
mockElement,
mockTemplates,
mockElements,
mockContents,
mockNewScope,
mockPromise,
linker;
beforeEach(function () {
mockTemplateRequest = jasmine.createSpy('$templateRequest');
mockSce = jasmine.createSpyObj('$sce', ['trustAsResourceUrl']);
mockCompile = jasmine.createSpy('$compile');
mockLog = jasmine.createSpyObj('$log', ['error', 'warn']);
mockScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
mockNewScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
mockElement = jasmine.createSpyObj('element', JQLITE_METHODS);
mockPromise = jasmine.createSpyObj('promise', ['then']);
mockTemplates = {};
mockElements = {};
mockContents = {};
mockTemplateRequest.andReturn(mockPromise);
mockCompile.andCallFake(function (toCompile) {
var html = typeof toCompile === 'string' ?
toCompile : toCompile.testHtml;
mockTemplates[html] = jasmine.createSpy('template');
mockElements[html] =
jasmine.createSpyObj('templateEl', JQLITE_METHODS);
mockTemplates[html].andReturn(mockElements[html]);
return mockTemplates[html];
});
mockSce.trustAsResourceUrl.andCallFake(function (url) {
return { trusted: url };
});
mockScope.$new.andReturn(mockNewScope);
mockElement.html.andCallFake(function (html) {
mockContents[html] =
jasmine.createSpyObj('contentsEl', JQLITE_METHODS);
mockContents[html].testHtml = html;
});
mockElement.contents.andCallFake(function () {
return mockContents[
mockElement.html.mostRecentCall.args[0]
];
});
linker = new TemplateLinker(
mockTemplateRequest,
mockSce,
mockCompile,
mockLog
);
});
it("resolves extension paths", function () {
expect(linker.getPath({
bundle: {
path: 'a',
resources: 'b'
},
templateUrl: 'c/d.html'
})).toEqual('a/b/c/d.html');
});
describe("when linking elements", function () {
var changeTemplate,
commentElement;
function findCommentElement() {
mockCompile.calls.forEach(function (call) {
var html = call.args[0];
if (html.indexOf("<!--") > -1) {
commentElement = mockElements[html];
}
});
}
beforeEach(function () {
changeTemplate = linker.link(mockScope, mockElement);
findCommentElement();
});
it("compiles a comment to use to replace element", function () {
expect(commentElement).toBeDefined();
});
it("initially replaces elements with comments", function () {
expect(mockElement.replaceWith)
.toHaveBeenCalledWith(commentElement);
});
it("provides a function to change templates", function () {
expect(changeTemplate).toEqual(jasmine.any(Function));
});
describe("and then changing templates", function () {
var testUrl,
testTemplate;
beforeEach(function () {
testUrl = "some/url/template.html";
testTemplate = "<div>Some template!</div>";
changeTemplate(testUrl);
mockPromise.then.mostRecentCall
.args[0](testTemplate);
});
it("loads templates using $templateRequest", function () {
expect(mockTemplateRequest).toHaveBeenCalledWith({
trusted: testUrl
}, false);
});
it("compiles element contents with a new scope", function () {
expect(mockCompile)
.toHaveBeenCalledWith(mockContents[testTemplate]);
expect(mockTemplates[testTemplate])
.toHaveBeenCalledWith(mockNewScope);
});
it("replaces comments with specified element", function () {
expect(commentElement.replaceWith)
.toHaveBeenCalledWith(mockElement);
});
it("inserts HTML content into the specified element", function () {
expect(mockElement.html)
.toHaveBeenCalledWith(testTemplate);
});
it("clears templates when called with undefined", function () {
expect(mockElement.replaceWith.callCount)
.toEqual(1);
changeTemplate(undefined);
expect(mockElement.replaceWith.callCount)
.toEqual(2);
expect(mockElement.replaceWith.mostRecentCall.args[0])
.toEqual(commentElement);
});
it("logs no warnings for nominal changes", function () {
expect(mockLog.warn).not.toHaveBeenCalled();
});
describe("which cannot be found", function () {
beforeEach(function () {
changeTemplate("some/bad/url");
// Reject the template promise
mockPromise.then.mostRecentCall.args[1]();
});
it("removes the element from the DOM", function () {
expect(mockElement.replaceWith.callCount)
.toEqual(2);
expect(
mockElement.replaceWith.mostRecentCall.args[0]
).toEqual(commentElement);
});
it("logs a warning", function () {
expect(mockLog.warn)
.toHaveBeenCalledWith(jasmine.any(String));
});
});
});
});
describe("when an initial template URL is provided", function () {
var testUrl;
beforeEach(function () {
testUrl = "some/test/url.html";
linker.link(mockScope, mockElement, testUrl);
});
it("does not remove the element initially", function () {
expect(mockElement.replaceWith)
.not.toHaveBeenCalled();
});
it("loads the specified template", function () {
expect(mockTemplateRequest).toHaveBeenCalledWith({
trusted: testUrl
}, false);
});
});
});
}
);

View File

@ -7,5 +7,6 @@
"gestures/GestureRepresenter",
"services/DndService",
"MCTInclude",
"MCTRepresentation"
]
"MCTRepresentation",
"TemplateLinker"
]

View File

@ -27,6 +27,12 @@
}
],
"representations": [
{
"key": "search-item",
"templateUrl": "templates/search-item.html"
}
],
"templates": [
{
"key": "search",
"templateUrl": "templates/search.html"
@ -34,10 +40,6 @@
{
"key": "search-menu",
"templateUrl": "templates/search-menu.html"
},
{
"key": "search-item",
"templateUrl": "templates/search-item.html"
}
],
"components": [

View File

@ -43,7 +43,7 @@
<a class="ui-symbol clear-icon"
ng-class="{content: !(ngModel.input === '' || ngModel.input === undefined)}"
ng-click="ngModel.input = ''; controller.search()">
&#xe607;
&#xe607;
</a>
<!-- Menu icon/button 'v' -->
@ -53,12 +53,12 @@
</a>
<!-- Menu -->
<mct-representation key="'search-menu'"
<mct-include key="'search-menu'"
class="menu-element search-menu-holder"
ng-class="{off: !toggle.isActive()}"
ng-model="ngModel"
ng-click="toggle.setState(true)">
</mct-representation>
</mct-include>
</div>
<!-- Active filter display -->
@ -71,7 +71,7 @@
&#xe607;
</a>
Filtered by: {{ ngModel.filtersString }}
Filtered by: {{ ngModel.filtersString }}
</div>

View File

@ -37,7 +37,8 @@
"services": [
{
"key": "telemetryFormatter",
"implementation": "TelemetryFormatter.js"
"implementation": "TelemetryFormatter.js",
"depends": [ "formatService", "DEFAULT_TIME_FORMAT" ]
},
{
"key": "telemetrySubscriber",
@ -63,4 +64,4 @@
}
]
}
}
}

View File

@ -36,6 +36,64 @@ define(
getRangeValue: ZERO
};
/**
* Provides metadata about telemetry associated with a
* given domain object.
*
* @typedef TelemetryMetadata
* @property {string} source the machine-readable identifier for
* the source of telemetry data for this object; used by
* {@link TelemetryService} implementations to determine
* whether or not they provide data for this object.
* @property {string} key the machine-readable identifier for
* telemetry data associated with this specific object,
* within that `source`.
* @property {TelemetryDomainMetadata[]} domains supported domain
* options for telemetry data associated with this object,
* to use in interpreting a {@link TelemetrySeries}
* @property {TelemetryRangeMetadata[]} ranges supported range
* options for telemetry data associated with this object,
* to use in interpreting a {@link TelemetrySeries}
*/
/**
* Provides metadata about range options within a telemetry series.
* Range options describe distinct properties within any given datum
* of a telemetry series; for instance, a telemetry series containing
* both raw and uncalibrated values may provide separate ranges for
* each.
*
* @typedef TelemetryRangeMetadata
* @property {string} key machine-readable identifier for this range
* @property {string} name human-readable name for this range
* @property {string} [units] human-readable units for this range
* @property {string} [format] data format for this range; usually,
* one of `number`, or `string`. If `undefined`,
* should presume to be a `number`. Custom formats
* may be indicated here.
*/
/**
* Provides metadata about domain options within a telemetry series.
* Domain options describe distinct properties within any given datum
* of a telemtry series; for instance, a telemetry series containing
* both spacecraft event time and earth received times may provide
* separate domains for each.
*
* Domains are typically used to represent timestamps in a telemetry
* series, but more generally may express any property which will
* have unique values for each datum in a series. It is this property
* which makes domains distinct from ranges, as it makes these values
* appropriate and meaningful for use to sort and bound a series.
*
* @typedef TelemetryDomainMetadata
* @property {string} key machine-readable identifier for this range
* @property {string} name human-readable name for this range
* @property {string} [system] machine-readable identifier for the
* time/date system associated with this domain;
* used by {@link DateService}
*/
/**
* A telemetry capability provides a means of requesting telemetry
* for a specific object, and for unwrapping the response (to get

View File

@ -22,14 +22,13 @@
/*global define,moment*/
define(
['moment'],
function (moment) {
[],
function () {
"use strict";
// Date format to use for domain values; in particular,
// use day-of-year instead of month/day
var DATE_FORMAT = "YYYY-DDD HH:mm:ss",
VALUE_FORMAT_DIGITS = 3;
var VALUE_FORMAT_DIGITS = 3;
/**
* The TelemetryFormatter is responsible for formatting (as text
@ -37,22 +36,31 @@ define(
* the range (usually value) of a data series.
* @memberof platform/telemetry
* @constructor
* @param {FormatService} formatService the service to user to format
* domain values
* @param {string} defaultFormatKey the format to request when no
* format has been otherwise specified
*/
function TelemetryFormatter() {
function TelemetryFormatter(formatService, defaultFormatKey) {
this.formatService = formatService;
this.defaultFormat = formatService.getFormat(defaultFormatKey);
}
/**
* Format a domain value.
* @param {number} v the domain value; a timestamp
* @param {number} v the domain value; usually, a timestamp
* in milliseconds since start of 1970
* @param {string} [key] the key which identifies the
* domain; if unspecified or unknown, this will
* be treated as a standard timestamp.
* @param {string} [key] a key which identifies the format
* to use
* @returns {string} a textual representation of the
* data and time, suitable for display.
*/
TelemetryFormatter.prototype.formatDomainValue = function (v, key) {
return isNaN(v) ? "" : moment.utc(v).format(DATE_FORMAT);
var formatter = (key === undefined) ?
this.defaultFormat :
this.formatService.getFormat(key);
return isNaN(v) ? "" : formatter.format(v);
};
/**

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