Merge remote-tracking branch 'origin/master' into frontend-1047

This commit is contained in:
Charles Hacskaylo 2016-07-21 08:53:23 -07:00
commit 883f999215
21 changed files with 636 additions and 161 deletions

2
app.js
View File

@ -42,6 +42,8 @@
process.exit(0);
}
app.disable('x-powered-by');
// Override bundles.json for HTTP requests
app.use('/' + BUNDLE_FILE, function (req, res) {
var bundles;

View File

@ -44,7 +44,8 @@ define(
periodically with the progress of an ongoing process.
*/
$scope.launchProgress = function (knownProgress) {
var model = {
var dialog,
model = {
title: "Progress Dialog Example",
progress: 0,
hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.",
@ -57,7 +58,7 @@ define(
label: "Cancel Operation",
callback: function () {
$log.debug("Operation cancelled");
dialogService.dismiss();
dialog.dismiss();
}
},
{
@ -77,7 +78,9 @@ define(
}
}
if (dialogService.showBlockingMessage(model)) {
dialog = dialogService.showBlockingMessage(model);
if (dialog) {
//Do processing here
model.actionText = "Processing 100 objects...";
if (knownProgress) {
@ -93,7 +96,8 @@ define(
Demonstrates launching an error dialog
*/
$scope.launchError = function () {
var model = {
var dialog,
model = {
title: "Error Dialog Example",
actionText: "Something happened, and it was not good.",
severity: "error",
@ -102,20 +106,21 @@ define(
label: "Try Again",
callback: function () {
$log.debug("Try Again Pressed");
dialogService.dismiss();
dialog.dismiss();
}
},
{
label: "Cancel",
callback: function () {
$log.debug("Cancel Pressed");
dialogService.dismiss();
dialog.dismiss();
}
}
]
};
dialog = dialogService.showBlockingMessage(model);
if (!dialogService.showBlockingMessage(model)) {
if (!dialog) {
$log.error("Could not display modal dialog");
}
};
@ -124,7 +129,8 @@ define(
Demonstrates launching an error dialog
*/
$scope.launchInfo = function () {
var model = {
var dialog,
model = {
title: "Info Dialog Example",
actionText: "This is an example of a blocking info" +
" dialog. This dialog can be used to draw the user's" +
@ -134,12 +140,14 @@ define(
label: "OK",
callback: function () {
$log.debug("OK Pressed");
dialogService.dismiss();
dialog.dismiss();
}
}
};
if (!dialogService.showBlockingMessage(model)) {
dialog = dialogService.showBlockingMessage(model);
if (!dialog) {
$log.error("Could not display modal dialog");
}
};

View File

@ -77,6 +77,7 @@ define([
'./platform/exporters/bundle',
'./platform/telemetry/bundle',
'./platform/features/clock/bundle',
'./platform/features/fixed/bundle',
'./platform/features/imagery/bundle',
'./platform/features/layout/bundle',
'./platform/features/pages/bundle',

View File

@ -23,6 +23,7 @@
define([
"./src/BrowseController",
"./src/PaneController",
"./src/InspectorPaneController",
"./src/BrowseObjectController",
"./src/MenuArrowController",
"./src/navigation/NavigationService",
@ -44,6 +45,7 @@ define([
], function (
BrowseController,
PaneController,
InspectorPaneController,
BrowseObjectController,
MenuArrowController,
NavigationService,
@ -124,6 +126,17 @@ define([
"depends": [
"$scope"
]
},
{
"key": "InspectorPaneController",
"implementation": InspectorPaneController,
"priority": "preferred",
"depends": [
"$scope",
"agentService",
"$window",
"navigationService"
]
}
],
"representations": [

View File

@ -57,7 +57,7 @@
ng-class="{ collapsed : !modelPaneTree.visible() }"></a>
<div class='holder holder-object-and-inspector abs' id='content-area'
ng-controller="PaneController as modelPaneInspect"
ng-controller="InspectorPaneController as modelPaneInspect"
ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'">
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>

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.
*****************************************************************************/
define(
["./PaneController"],
function (PaneController) {
/**
* Pane controller that reveals inspector, if hidden, when object
* switches to edit mode.
*
* @param $scope
* @param agentService
* @param $window
* @param navigationService
* @constructor
*/
function InspectorPaneController($scope, agentService, $window, navigationService) {
PaneController.call(this, $scope, agentService, $window);
var statusListener,
self = this;
function showInspector(statuses) {
if (statuses.indexOf('editing') !== -1 && !self.visible()) {
self.toggle();
}
}
function attachStatusListener(domainObject) {
// Remove existing status listener if existing
if (statusListener) {
statusListener();
}
if (domainObject.hasCapability("status")) {
statusListener = domainObject.getCapability("status").listen(showInspector);
}
return statusListener;
}
var domainObject = navigationService.getNavigation();
if (domainObject) {
attachStatusListener(domainObject);
}
var navigationListener = navigationService.addListener(attachStatusListener);
$scope.$on("$destroy", function () {
statusListener();
navigationListener();
});
}
InspectorPaneController.prototype = Object.create(PaneController.prototype);
return InspectorPaneController;
}
);

View File

@ -0,0 +1,96 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/InspectorPaneController"],
function (InspectorPaneController) {
describe("The InspectorPaneController", function () {
var mockScope,
mockAgentService,
mockDomainObject,
mockWindow,
mockStatusCapability,
mockNavigationService,
mockNavigationUnlistener,
mockStatusUnlistener,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$on"]);
mockWindow = jasmine.createSpyObj("$window", ["open"]);
mockAgentService = jasmine.createSpyObj(
"agentService",
["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"]
);
mockNavigationUnlistener = jasmine.createSpy("navigationUnlistener");
mockNavigationService = jasmine.createSpyObj(
"navigationService",
["getNavigation", "addListener"]
);
mockNavigationService.addListener.andReturn(mockNavigationUnlistener);
mockStatusUnlistener = jasmine.createSpy("statusUnlistener");
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
["listen"]
);
mockStatusCapability.listen.andReturn(mockStatusUnlistener);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
[
'getId',
'getModel',
'getCapability',
'hasCapability'
]
);
mockDomainObject.getId.andReturn("domainObject");
mockDomainObject.getModel.andReturn({});
mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andReturn(mockStatusCapability);
controller = new InspectorPaneController(mockScope, mockAgentService, mockWindow, mockNavigationService);
});
it("listens for changes to navigation and attaches a status" +
" listener", function () {
expect(mockNavigationService.addListener).toHaveBeenCalledWith(jasmine.any(Function));
mockNavigationService.addListener.mostRecentCall.args[0](mockDomainObject);
expect(mockStatusCapability.listen).toHaveBeenCalledWith(jasmine.any(Function));
});
it("if hidden, shows the inspector when domain object switches to" +
" edit mode", function () {
controller.toggle();
// test pre-condition that inspector is hidden
expect(controller.visible()).toBe(false);
mockNavigationService.addListener.mostRecentCall.args[0](mockDomainObject);
mockStatusCapability.listen.mostRecentCall.args[0](["editing"]);
expect(controller.visible()).toBe(true);
});
});
}
);

View File

@ -39,25 +39,28 @@ define(
this.overlayService = overlayService;
this.$q = $q;
this.$log = $log;
this.overlay = undefined;
this.dialogVisible = false;
this.activeOverlay = undefined;
}
// Stop showing whatever overlay is currently active
// (e.g. because the user hit cancel)
DialogService.prototype.dismiss = function () {
var overlay = this.overlay;
if (overlay) {
/**
* @private
*/
DialogService.prototype.dismissOverlay = function (overlay) {
//Dismiss the overlay
overlay.dismiss();
//If dialog is the current active one, dismiss it
if (overlay === this.activeOverlay) {
this.activeOverlay = undefined;
}
this.dialogVisible = false;
};
DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
// We will return this result as a promise, because user
// input is asynchronous.
var deferred = this.$q.defer(),
self = this;
self = this,
overlay;
// Confirm function; this will be passed in to the
// overlay-dialog template and associated with a
@ -65,9 +68,7 @@ define(
function confirm(value) {
// Pass along the result
deferred.resolve(resultGetter ? resultGetter() : value);
// Stop showing the dialog
self.dismiss();
self.dismissOverlay(overlay);
}
// Cancel function; this will be passed in to the
@ -75,7 +76,7 @@ define(
// Cancel or X button click
function cancel() {
deferred.reject();
self.dismiss();
self.dismissOverlay(overlay);
}
// Add confirm/cancel callbacks
@ -85,15 +86,11 @@ define(
if (this.canShowDialog(model)) {
// Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM
this.overlay = this.overlayService.createOverlay(
overlay = this.activeOverlay = this.overlayService.createOverlay(
key,
model,
typeClass || "t-dialog"
);
// Track that a dialog is already visible, to
// avoid spawning multiple dialogs at once.
this.dialogVisible = true;
} else {
deferred.reject();
}
@ -156,7 +153,7 @@ define(
* otherwise
*/
DialogService.prototype.canShowDialog = function (dialogModel) {
if (this.dialogVisible) {
if (this.activeOverlay) {
// Only one dialog should be shown at a time.
// The application design should be such that
// we never even try to do this.
@ -183,6 +180,11 @@ define(
* button is clicked
*/
/**
* @typedef DialogHandle
* @property {function} dismiss a function to dismiss the given dialog
*/
/**
* A description of the model options that may be passed to the
* showBlockingMessage method. Note that the DialogModel desribed
@ -222,21 +224,26 @@ define(
* the user can take if necessary
* @param {DialogModel} dialogModel defines options for the dialog
* @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
* @returns {boolean}
* @returns {boolean | {DialogHandle}}
*/
DialogService.prototype.showBlockingMessage = function (dialogModel) {
if (this.canShowDialog(dialogModel)) {
// Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM
this.overlay = this.overlayService.createOverlay(
var self = this,
overlay = this.overlayService.createOverlay(
"overlay-blocking-message",
dialogModel,
"t-dialog-sm"
);
// Track that a dialog is already visible, to
// avoid spawning multiple dialogs at once.
this.dialogVisible = true;
return true;
this.activeOverlay = overlay;
return {
dismiss: function () {
self.dismissOverlay(overlay);
}
};
} else {
return false;
}

View File

@ -122,7 +122,7 @@ define(
it("invokes the overlay service with the correct parameters when" +
" a blocking dialog is requested", function () {
var dialogModel = {};
expect(dialogService.showBlockingMessage(dialogModel)).toBe(true);
expect(dialogService.showBlockingMessage(dialogModel)).not.toBe(false);
expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
"overlay-blocking-message",
dialogModel,
@ -130,6 +130,45 @@ define(
);
});
describe("the blocking message dialog", function () {
var dialogModel = {};
var dialogHandle;
beforeEach(function () {
dialogHandle = dialogService.showBlockingMessage(dialogModel);
});
it("returns a handle to the dialog", function () {
expect(dialogHandle).not.toBe(undefined);
});
it("dismissing the dialog dismisses the overlay", function () {
dialogHandle.dismiss();
expect(mockOverlay.dismiss).toHaveBeenCalled();
});
it("individual dialogs can be dismissed", function () {
var secondDialogHandle,
secondMockOverlay;
dialogHandle.dismiss();
secondMockOverlay = jasmine.createSpyObj(
"overlay",
["dismiss"]
);
mockOverlayService.createOverlay.andReturn(secondMockOverlay);
secondDialogHandle = dialogService.showBlockingMessage(dialogModel);
//Dismiss the first dialog. It should only dismiss if it
// is active
dialogHandle.dismiss();
expect(secondMockOverlay.dismiss).not.toHaveBeenCalled();
secondDialogHandle.dismiss();
expect(secondMockOverlay.dismiss).toHaveBeenCalled();
});
});
});
}
);

View File

@ -1,10 +1,11 @@
define([], function () {
function SaveInProgressDialog(dialogService) {
this.dialogService = dialogService;
this.dialog = undefined;
}
SaveInProgressDialog.prototype.show = function () {
this.dialogService.showBlockingMessage({
this.dialog = this.dialogService.showBlockingMessage({
title: "Saving...",
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
unknownProgress: true,
@ -13,7 +14,9 @@ define([], function () {
};
SaveInProgressDialog.prototype.hide = function () {
this.dialogService.dismiss();
if (this.dialog) {
this.dialog.dismiss();
}
};
return SaveInProgressDialog;

View File

@ -70,7 +70,7 @@ define(
};
dialogService = jasmine.createSpyObj(
"dialogService",
["showBlockingMessage", "dismiss"]
["showBlockingMessage"]
);
mockDomainObject.hasCapability.andReturn(true);
@ -111,17 +111,28 @@ define(
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
});
describe("a blocking dialog", function () {
var mockDialogHandle;
beforeEach(function () {
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
dialogService.showBlockingMessage.andReturn(mockDialogHandle);
});
it("shows a dialog while saving", function () {
mockEditorCapability.save.andReturn(new Promise(function () {}));
mockEditorCapability.save.andReturn(new Promise(function () {
}));
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(dialogService.dismiss).not.toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
});
it("hides a dialog when saving is complete", function () {
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(dialogService.dismiss).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
});
});
});

View File

@ -101,8 +101,7 @@ define(
"dialogService",
[
"getUserInput",
"showBlockingMessage",
"dismiss"
"showBlockingMessage"
]
);
mockDialogService.getUserInput.andReturn(mockPromise(undefined));
@ -171,14 +170,22 @@ define(
expect(mockDialogService.getUserInput).toHaveBeenCalled();
});
it("shows a blocking dialog while waiting for save", function () {
describe("a blocking dialog", function () {
var mockDialogHandle;
beforeEach(function () {
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
});
it("indicates that a save is taking place", function () {
mockEditorCapability.save.andReturn(new Promise(function () {}));
action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogService.dismiss).not.toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
});
it("hides the blocking dialog after saving", function () {
it("is hidden after saving", function () {
var mockCallback = jasmine.createSpy();
action.perform().then(mockCallback);
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
@ -186,10 +193,10 @@ define(
return mockCallback.calls.length > 0;
});
runs(function () {
expect(mockDialogService.dismiss).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
});
});
});
});
}
);

View File

@ -259,7 +259,7 @@ define([
"implementation": ClickAwayController,
"depends": [
"$document",
"$timeout"
"$scope"
]
},
{

View File

@ -54,17 +54,17 @@ define(
};
$scope.maximize = function (notification) {
if (notification.model.severity !== "info") {
var dialog;
notification.model.cancel = function () {
dialogService.dismiss();
dialog.dismiss();
};
//If the notification is dismissed by the user, close
// the dialog.
notification.onDismiss(function () {
dialogService.dismiss();
dialog.dismiss();
});
dialogService.showBlockingMessage(notification.model);
dialog = dialogService.showBlockingMessage(notification.model);
}
};
}

View File

@ -34,7 +34,7 @@ define(
* @param $scope the scope in which this controller is active
* @param $document the document element, injected by Angular
*/
function ClickAwayController($document, $timeout) {
function ClickAwayController($document, $scope) {
var self = this;
this.state = false;
@ -44,7 +44,7 @@ define(
// `clickaway` action occurs after `toggle` if `toggle` is
// triggered by a click/mouseup.
this.clickaway = function () {
$timeout(function () {
$scope.$apply(function () {
self.deactivate();
});
};

View File

@ -26,7 +26,7 @@ define(
describe("The click-away controller", function () {
var mockDocument,
mockTimeout,
mockScope,
controller;
beforeEach(function () {
@ -34,10 +34,11 @@ define(
"$document",
["on", "off"]
);
mockTimeout = jasmine.createSpy('timeout');
mockScope = jasmine.createSpyObj('$scope', ['$apply']);
controller = new ClickAwayController(
mockDocument,
mockTimeout
mockScope
);
});
@ -77,18 +78,15 @@ define(
});
it("deactivates and detaches listener on document click", function () {
var callback, timeout;
var callback, apply;
controller.setState(true);
callback = mockDocument.on.mostRecentCall.args[1];
callback();
timeout = mockTimeout.mostRecentCall.args[0];
timeout();
apply = mockScope.$apply.mostRecentCall.args[0];
apply();
expect(controller.isActive()).toEqual(false);
expect(mockDocument.off).toHaveBeenCalledWith("mouseup", callback);
});
});
}
);

View File

@ -49,9 +49,6 @@ define(
//Launch the message list dialog with the models
// from the notifications
messages: notificationService.notifications
},
cancel: function () {
dialogService.dismiss();
}
});

View File

@ -54,22 +54,7 @@ define(
expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list');
expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].dialog).toBeDefined();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
//Invoke the cancel callback
mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
expect(mockDialogService.dismiss).toHaveBeenCalled();
});
it("provides a means of dismissing the message list", function () {
expect(mockScope.showNotificationsList).toBeDefined();
mockScope.showNotificationsList();
expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
//Invoke the cancel callback
mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
expect(mockDialogService.dismiss).toHaveBeenCalled();
});
});
}
);

View File

@ -86,7 +86,9 @@ define(
severity: "info"
});
} else if (phase.toLowerCase() === "copying") {
this.dialogService.dismiss();
if (this.dialog) {
this.dialog.dismiss();
}
if (!this.notification) {
this.notification = this.notificationService
.notify({
@ -115,7 +117,8 @@ define(
}
function error(errorDetails) {
var errorMessage = {
var errorDialog,
errorMessage = {
title: "Error copying objects.",
severity: "error",
hint: errorDetails.message,
@ -123,12 +126,12 @@ define(
options: [{
label: "OK",
callback: function () {
self.dialogService.dismiss();
errorDialog.dismiss();
}
}]
};
self.dialogService.dismiss();
self.dialog.dismiss();
if (self.notification) {
self.notification.dismiss(); // Clear the progress notification
}
@ -136,7 +139,7 @@ define(
//Show a minimized notification of error for posterity
self.notificationService.notify(errorMessage);
//Display a blocking message
self.dialogService.showBlockingMessage(errorMessage);
errorDialog = self.dialogService.showBlockingMessage(errorMessage);
}
function notification(details) {

View File

@ -44,6 +44,7 @@ define(
notificationService,
notification,
dialogService,
mockDialog,
mockLog,
abstractComposePromise,
progress = {phase: "copying", totalObjects: 10, processed: 1};
@ -120,9 +121,12 @@ define(
.andReturn(locationServicePromise);
dialogService = jasmine.createSpyObj('dialogService',
['showBlockingMessage', 'dismiss']
['showBlockingMessage']
);
mockDialog = jasmine.createSpyObj("dialog", ["dismiss"]);
dialogService.showBlockingMessage.andReturn(mockDialog);
notification = jasmine.createSpyObj('notification',
['dismiss', 'model']
);

View File

@ -0,0 +1,222 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"text!../layout/res/templates/fixed.html",
'legacyRegistry'
], function (
fixedTemplate,
legacyRegistry
) {
legacyRegistry.register("platform/features/fixed", {
"name": "Fixed position components.",
"description": "Plug in adding Fixed Position object type.",
"extensions": {
"views": [
{
"key": "fixed-display",
"name": "Fixed Position Display",
"glyph": "3",
"type": "telemetry.fixed",
"template": fixedTemplate,
"uses": [
"composition"
],
"editable": true,
"toolbar": {
"sections": [
{
"items": [
{
"method": "add",
"glyph": "\u002b",
"control": "menu-button",
"text": "Add",
"options": [
{
"name": "Box",
"glyph": "\u00e0",
"key": "fixed.box"
},
{
"name": "Line",
"glyph": "\u00e2",
"key": "fixed.line"
},
{
"name": "Text",
"glyph": "\u00e4",
"key": "fixed.text"
},
{
"name": "Image",
"glyph": "\u00e3",
"key": "fixed.image"
}
]
}
]
},
{
"items": [
{
"method": "order",
"glyph": "á",
"control": "menu-button",
"options": [
{
"name": "Move to Top",
"glyph": "\u00eb",
"key": "top"
},
{
"name": "Move Up",
"glyph": "\u005e",
"key": "up"
},
{
"name": "Move Down",
"glyph": "\u0076",
"key": "down"
},
{
"name": "Move to Bottom",
"glyph": "\u00ee",
"key": "bottom"
}
]
},
{
"property": "fill",
"glyph": "",
"control": "color"
},
{
"property": "stroke",
"glyph": "â",
"control": "color"
},
{
"property": "color",
"glyph": "ä",
"mandatory": true,
"control": "color"
},
{
"property": "url",
"glyph": "ã",
"control": "dialog-button",
"title": "Image Properties",
"dialog": {
"control": "textfield",
"name": "Image URL",
"required": true
}
},
{
"property": "text",
"glyph": "G",
"control": "dialog-button",
"title": "Text Properties",
"dialog": {
"control": "textfield",
"name": "Text",
"required": true
}
},
{
"method": "showTitle",
"glyph": "ç",
"control": "button",
"description": "Show telemetry element title."
},
{
"method": "hideTitle",
"glyph": "å",
"control": "button",
"description": "Hide telemetry element title."
}
]
},
{
"items": [
{
"method": "remove",
"control": "button",
"glyph": "Z"
}
]
}
]
}
}
],
"types": [
{
"key": "telemetry.fixed",
"name": "Fixed Position Display",
"glyph": "3",
"description": "A panel for collecting telemetry" +
" elements in a fixed position display.",
"priority": 899,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"properties": [
{
"name": "Layout Grid",
"control": "composite",
"items": [
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssclass": "l-small l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssclass": "l-small l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",
"property": "layoutGrid",
"conversion": "number[]"
}
],
"views": [
"fixed-display"
]
}
]
}
});
});