Compare commits

...

20 Commits

Author SHA1 Message Date
1ea23e53e7 [CLI] Auto-focus 2015-10-08 15:06:35 -07:00
f6c66248c8 [CLI] Add license header 2015-10-08 15:03:19 -07:00
9bffd0c5d5 Merge remote-tracking branch 'origin/open147' into cli 2015-10-08 14:56:24 -07:00
1ddfa671c7 [CLI] Visually distinguish user input 2015-10-08 14:52:22 -07:00
886ac8e859 [CLI] Distinguish links 2015-10-08 14:48:50 -07:00
7e121dc647 [CLI] Clean up command formats 2015-10-08 14:35:43 -07:00
83ce018bef [Common UI] Avoid infinite digest 2015-10-08 14:35:21 -07:00
3b6b4a5694 [Browse] Separate out 'back' as an action. 2015-10-08 14:18:01 -07:00
f8e0dbd5b7 [Create] Add category for create actions 2015-10-08 14:12:36 -07:00
af5449b78f [CLI] Hide missing descriptions 2015-10-08 14:01:25 -07:00
c9df2db962 [CLI] Add some shadowing 2015-10-08 13:58:58 -07:00
bc757c09d4 [Browse] Add key to Fullscreen action 2015-10-08 13:58:49 -07:00
7b3fe0e1dc [CLI] Autoscroll, re-echo objects on empty input 2015-10-08 13:49:02 -07:00
0609c7e0ac [Common UI] Handle out-of-range scroll values
When user code specifies an out-of-range scroll value,
this will get clamped to scrollWidth or scrollHeight;
so, publish this value back into scope if that occurs.
2015-10-08 13:48:40 -07:00
1a802d1e7f [CLI] Hide irrelevant info 2015-10-08 12:58:57 -07:00
efd2c47a3f [CLI] Begin supporting commands 2015-10-08 12:55:06 -07:00
45fb644658 [CLI] Add some styles 2015-10-08 11:21:37 -07:00
b0d72c49bc [CLI] Show navigated object 2015-10-08 11:10:24 -07:00
e2b4c87c03 [CLI] Receive input 2015-10-08 10:55:27 -07:00
f1f8439726 [CLI] Initial commit; add bundle 2015-10-08 10:47:52 -07:00
12 changed files with 355 additions and 10 deletions

22
ideas/cli/bundle.json Normal file
View File

@ -0,0 +1,22 @@
{
"extensions": {
"routes": [
{
"when": "/cli",
"templateUrl": "templates/cli.html"
}
],
"controllers": [
{
"key": "CLIController",
"implementation": "CLIController.js",
"depends": [ "$scope", "navigationService", "objectService" ]
}
],
"stylesheets": [
{
"stylesheetUrl": "stylesheets/cli.css"
}
]
}
}

View File

@ -0,0 +1,47 @@
.iw-container {
position: absolute;
bottom: 30px;
left: 0;
top: 0;
right: 0;
}
.iw-stdin {
position: absolute;
bottom: 0;
left: 0;
height: 30px;
width: calc(100% - 8px);
margin: 4px;
font-family: monospace;
font-size: 16px;
}
.iw-stdout {
position: absolute;
left: 6px;
right: 6px;
top: 12px;
bottom: 44px;
border: 1px #211 solid;
border-radius: 12px;
padding: 8px;
background: #302;
overflow: auto;
box-shadow: inset 0px 2px 8px 0px rgba(0, 0, 0, 0.5);
}
.iw-stdout span {
display: block;
font-family: monospace;
color: #FD9;
font-size: 16px;
margin-top: 2px;
margin-bottom: 2px;
min-height: 1em;
white-space: pre;
}
.iw-stdout span.iw-user-input {
color: gray;
}

View File

@ -0,0 +1,15 @@
<div class="abs holder-all browse-mode" ng-controller="CLIController">
<div class="iw-container">
<div class="iw-stdout" mct-scroll-y="stdoutScroll">
<span ng-repeat="line in stdout track by $index"
class="{{line.cssClass}}"
ng-bind="line.text">
</span>
</div>
<form ng-submit="enter(stdin)">
<input type="text" class="iw-stdin" ng-model="stdin" autofocus>
</form>
</div>
<mct-include key="'bottombar'"></mct-include>
</div>

View File

@ -0,0 +1,177 @@
/*****************************************************************************
* 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';
return function CLIController($scope, navigationService, objectService) {
var unlistenToMutation,
currentComposition = [];
function print(str, cssClass) {
$scope.stdout.push({
text: str,
cssClass: cssClass
});
$scope.stdoutScroll = Number.MAX_VALUE;
}
function pad(str, length) {
while (str.length < length) {
str += " ";
}
return str;
}
function summarize(domainObject) {
var type = domainObject.getCapability("type"),
typeName = type ? type.getName() : "Object",
location = domainObject.getCapability('location'),
isLink = (location && location.isLink()),
suffix = isLink ? " (link)" : "";
return "[" + typeName + "] " + domainObject.getModel().name + suffix;
}
function printComposition(domainObject) {
return domainObject.useCapability("composition").then(function (c) {
currentComposition = c;
c.forEach(function (childObject, i) {
print(i + ") " + summarize(childObject));
});
});
}
function printObject(domainObject, callback) {
// Exclude the root object; nobody wants to see that
if (domainObject.hasCapability("context")) {
print(summarize(domainObject));
}
if (domainObject.hasCapability('composition')) {
printComposition(domainObject).then(callback);
} else {
callback();
}
}
function unlisten() {
if (unlistenToMutation) {
unlistenToMutation();
unlistenToMutation = undefined;
}
}
function navChange(domainObject) {
unlisten();
unlistenToMutation = domainObject.getCapability("mutation")
.listen(function () { printObject(domainObject); });
printObject(domainObject);
}
function findTarget(id) {
if (id === "this") {
return navigationService.getNavigation();
} else {
return currentComposition[parseInt(id, 10)];
}
}
function listActions(domainObject, index) {
// Don't show actions for the root
if (!domainObject.hasCapability('context')) {
return;
}
domainObject.getCapability('action').getActions().forEach(function (a) {
var metadata = a.getMetadata(),
desc = metadata.description,
suffix = index !== undefined ? (" " + index) : "";
print(pad(metadata.key + suffix, 32) + (desc || ""));
});
}
function performAction(domainObject, action) {
domainObject.getCapability('action').perform(action);
}
function handleInput(input) {
var parts = input.split(" "),
targetObject;
if (input.length === 0) {
targetObject = navigationService.getNavigation();
if (targetObject) {
printObject(targetObject, function () {
print("");
listActions(targetObject);
});
}
return;
} else if (parts.length === 1) {
if (isNaN(parseInt(parts[0], 10))) {
targetObject = navigationService.getNavigation();
performAction(targetObject, parts[0]);
return;
}
targetObject = findTarget(parts[0]);
if (targetObject) {
listActions(targetObject, parts[0]);
return;
}
} else if (parts.length === 2) {
targetObject = findTarget(parts[1]);
if (targetObject) {
performAction(targetObject, parts[0]);
return;
}
}
// Any parse-able input should have returned already.
print("SYNTAX ERROR. READY.");
}
if (!navigationService.getNavigation()) {
objectService.getObjects(["ROOT"]).then(function (objects) {
navigationService.setNavigation(objects.ROOT);
});
}
navigationService.addListener(navChange);
$scope.stdout = [];
$scope.stdin = "";
$scope.stdoutScroll = 0;
$scope.enter = function (input) {
$scope.stdin = "";
print("");
print(input, "iw-user-input");
print("");
handleInput(input);
};
$scope.$on("$destroy", function () {
navigationService.removeListener(navChange);
unlisten();
});
};
});

View File

@ -105,9 +105,15 @@
"actions": [ "actions": [
{ {
"key": "navigate", "key": "navigate",
"description": "Browse to this object.",
"implementation": "navigation/NavigateAction.js", "implementation": "navigation/NavigateAction.js",
"depends": [ "navigationService", "$q" ] "depends": [ "navigationService", "$q" ]
}, },
{
"key": "back",
"implementation": "navigation/BackAction.js",
"description": "Navigate to the parent of this object."
},
{ {
"key": "window", "key": "window",
"name": "Open In New Tab", "name": "Open In New Tab",

View File

@ -24,7 +24,7 @@
<a <a
class='type-icon icon ui-symbol s-back' class='type-icon icon ui-symbol s-back'
ng-show="context.getPath().length > 2" ng-show="context.getPath().length > 2"
ng-click="context.getParent().getCapability('action').perform('navigate')"> ng-click="domainObject.getCapability('action').perform('back')">
{ {
</a> </a>

View File

@ -53,11 +53,12 @@ define(
*/ */
function CreateAction(type, parent, context, dialogService, creationService, policyService) { function CreateAction(type, parent, context, dialogService, creationService, policyService) {
this.metadata = { this.metadata = {
key: 'create', key: 'create.' + type.getKey(),
glyph: type.getGlyph(), glyph: type.getGlyph(),
name: type.getName(), name: type.getName(),
type: type.getKey(), type: type.getKey(),
description: type.getDescription(), description: type.getDescription(),
category: [ 'creation' ],
context: context context: context
}; };

View File

@ -63,7 +63,10 @@ define(
// domain object to serve as the container for the // domain object to serve as the container for the
// newly-created object (although the user may later // newly-created object (although the user may later
// make a different selection) // make a different selection)
if (key !== 'create' || !destination) { if (!destination) {
return [];
}
if (context.category && context.category !== "creation") {
return []; return [];
} }

View File

@ -41,7 +41,7 @@ define(
// Update the set of Create actions // Update the set of Create actions
function refreshActions() { function refreshActions() {
$scope.createActions = $scope.action ? $scope.createActions = $scope.action ?
$scope.action.getActions('create') : $scope.action.getActions({ category: "creation" }) :
[]; [];
} }

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*/
/**
* Module defining NavigateAction. Created by vwoeltje on 11/10/14.
*/
define(
[],
function () {
"use strict";
/**
* The `back` action navigates to the contextual parent of a
* specific domain object.
* @memberof platform/commonUI/browse
* @constructor
* @implements {Action}
*/
function BackAction(context) {
this.domainObject = context.domainObject;
}
/**
* Navigate to the object described in the context.
* @returns {Promise} a promise that is resolved once the
* navigation has been updated
*/
BackAction.prototype.perform = function () {
var parent = this.domainObject.getCapability("context")
.getParent();
return parent.getCapability("action").perform("navigate");
};
/**
* Navigate as an action is only applicable when a domain object
* is described in the action context.
* @param {ActionContext} context the context in which the action
* will be performed
* @returns {boolean} true if applicable
*/
BackAction.appliesTo = function (context) {
return context.domainObject !== undefined &&
context.domainObject.hasCapability("context");
};
return BackAction;
}
);

View File

@ -53,9 +53,10 @@ define(
// based on whether or not we are currently // based on whether or not we are currently
// full screen. // full screen.
var metadata = Object.create(FullscreenAction); var metadata = Object.create(FullscreenAction);
metadata.key = "fullscreen";
metadata.glyph = screenfull.isFullscreen ? "_" : "z"; metadata.glyph = screenfull.isFullscreen ? "_" : "z";
metadata.description = screenfull.isFullscreen ? metadata.description = screenfull.isFullscreen ?
EXIT_FULLSCREEN : ENTER_FULLSCREEN; EXIT_FULLSCREEN : ENTER_FULLSCREEN;
metadata.group = "windowing"; metadata.group = "windowing";
metadata.context = this.context; metadata.context = this.context;
return metadata; return metadata;

View File

@ -49,17 +49,22 @@ define(
var expr = attrs[attribute], var expr = attrs[attribute],
parsed = $parse(expr); parsed = $parse(expr);
// Set the element's scroll to match the scope's state
function updateElement(value) {
element[0][property] = value;
}
// Handle event; assign to scroll state to scope // Handle event; assign to scroll state to scope
function updateScope() { function updateScope() {
parsed.assign(scope, element[0][property]); parsed.assign(scope, element[0][property]);
scope.$apply(expr); scope.$apply(expr);
} }
// Set the element's scroll to match the scope's state
function updateElement(value) {
element[0][property] = value;
// Some values may be out of range for the scroll bar,
// so publish the real scroll value back into scope.
if (element[0][property] !== value) {
parsed.assign(scope, element[0][property]);
}
}
// Initialize state in scope // Initialize state in scope
parsed.assign(scope, element[0][property]); parsed.assign(scope, element[0][property]);