Adds search field and jump-to a device UI.

Adds a search field in the network UI and a jump-to level menu. This
allows users to quickly find a device on the canvas or jump to a
certain mode/zoom-level.

Adds animation to smooth out the transition from the current viewport
to a viewport centered on the searched for device or zoom-level.

* Adds animation FSM and changes the 0 hot key to use it
* Adds jump to animation
* Adds search bar type ahead
* Adds jump animation to search and jump-to menus
* Adds keybinding FSM
* Updates the dropdown when devices are added/edit/removed
* Highlights the searched for host
This commit is contained in:
Ben Thomasson
2018-02-06 11:43:04 -05:00
parent 00a9283e32
commit 6f3bf4fd1b
27 changed files with 661 additions and 170 deletions

View File

@@ -19,6 +19,8 @@ var test_fsm = require('./test.fsm.js');
var util = require('./util.js');
var models = require('./models.js');
var messages = require('./messages.js');
var animations = require('./animations.js');
var keybindings = require('./keybindings.fsm.js');
var svg_crowbar = require('./svg-crowbar.js');
var ReconnectingWebSocket = require('reconnectingwebsocket');
@@ -106,6 +108,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.message_id_seq = util.natural_numbers(0);
$scope.stream_id_seq = util.natural_numbers(0);
$scope.test_result_id_seq = util.natural_numbers(0);
$scope.animation_id_seq = util.natural_numbers(0);
$scope.overall_toolbox_collapsed = false;
$scope.time_pointer = -1;
$scope.frame = 0;
@@ -124,6 +127,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.test_results = [];
$scope.test_errors = [];
$scope.streams = [];
$scope.animations = [];
$scope.view_port = {'x': 0,
'y': 0,
'width': 0,
@@ -131,6 +135,10 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.trace_id_seq = util.natural_numbers(0);
$scope.trace_order_seq = util.natural_numbers(0);
$scope.trace_id = $scope.trace_id_seq();
$scope.jump = {from_x: 0,
from_y: 0,
to_x: 0,
to_y: 0};
$scope.send_trace_message = function (message) {
if (!$scope.recording) {
@@ -150,9 +158,30 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
}
};
$scope.onKeyDown = function ($event) {
if ($scope.recording) {
$scope.send_control_message(new messages.KeyEvent($scope.client_id,
$event.key,
$event.keyCode,
$event.type,
$event.altKey,
$event.shiftKey,
$event.ctrlKey,
$event.metaKey,
$scope.trace_id));
}
$scope.last_event = $event;
$scope.last_key = $event.key;
$scope.last_key_code = $event.keyCode;
$scope.first_channel.send('KeyDown', $event);
$scope.$apply();
$event.preventDefault();
};
//Define the FSMs
$scope.null_controller = new fsm.FSMController($scope, "null_fsm", null_fsm.Start, $scope);
$scope.hotkeys_controller = new fsm.FSMController($scope, "hotkeys_fsm", hotkeys.Start, $scope);
$scope.keybindings_controller = new fsm.FSMController($scope, "keybindings_fsm", keybindings.Start, $scope);
$scope.view_controller = new fsm.FSMController($scope, "view_fsm", view.Start, $scope);
$scope.device_detail_controller = new fsm.FSMController($scope, "device_detail_fsm", device_detail_fsm.Start, $scope);
$scope.move_controller = new fsm.FSMController($scope, "move_fsm", move.Start, $scope);
@@ -202,21 +231,28 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
.then(function(response) {
let hosts = response.data.results;
for(var i = 0; i<hosts.length; i++){
let host = hosts[i];
console.log(host);
host.data = jsyaml.safeLoad(host.variables);
if (host.data.type == undefined) {
host.data.type = 'unknown';
try {
let host = hosts[i];
console.log(host);
if (host.variables !== "") {
host.data = jsyaml.safeLoad(host.variables);
if (host.data.type === undefined) {
host.data.type = 'unknown';
}
if (host.data.name === undefined) {
host.data.name = host.name;
}
var device = new models.Device(0, host.data.name, 0, 0, host.data.type, host.id, host.variables);
device.icon = true;
$scope.inventory_toolbox.items.push(device);
}
} catch (error) {
console.log(error);
}
if (host.data.name == undefined) {
host.data.name = host.name;
}
var device = new models.Device(0, host.data.name, 0, 0, host.data.type, host.id, host.variables);
device.icon = true;
$scope.inventory_toolbox.items.push(device);
}
})
.catch(({data, status}) => {
console.log([data, status]);
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get host data: ' + status });
});
}
@@ -269,9 +305,13 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.mode_controller = new fsm.FSMController($scope, "mode_fsm", mode_fsm.Start, $scope);
//Wire up the FSMs
$scope.view_controller.delegate_channel = new fsm.Channel($scope.view_controller,
$scope.keybindings_controller.delegate_channel = new fsm.Channel($scope.keybindings_controller,
$scope.hotkeys_controller,
$scope);
$scope.view_controller.delegate_channel = new fsm.Channel($scope.view_controller,
$scope.keybindings_controller,
$scope);
$scope.device_detail_controller.delegate_channel = new fsm.Channel($scope.device_detail_controller,
$scope.view_controller,
$scope);
@@ -354,6 +394,24 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
g.setAttribute('transform','translate(' + $scope.panX + ',' + $scope.panY + ') scale(' + $scope.current_scale + ')');
};
$scope.to_virtual_coordinates = function (b_x, b_y) {
var v_x = (b_x - $scope.panX) / $scope.current_scale;
var v_y = (b_y - $scope.panY) / $scope.current_scale;
return {x: v_x, y: v_y};
};
$scope.to_browser_coordinates = function (v_x, v_y) {
var b_x = (v_x * $scope.current_scale) + $scope.panX;
var b_y = (v_y * $scope.current_scale) + $scope.panY;
return {x: b_x, y: b_y};
};
$scope.to_pan = function (v_x, v_y) {
var p_x = v_x * $scope.current_scale * -1;
var p_y = v_y * $scope.current_scale * -1;
return {x: p_x, y: p_y};
};
$scope.clear_selections = function () {
var i = 0;
@@ -565,41 +623,22 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$event.preventDefault();
};
$scope.onKeyDown = function ($event) {
if ($scope.recording) {
$scope.send_control_message(new messages.KeyEvent($scope.client_id,
$event.key,
$event.keyCode,
$event.type,
$event.altKey,
$event.shiftKey,
$event.ctrlKey,
$event.metaKey,
$scope.trace_id));
}
$scope.last_event = $event;
$scope.last_key = $event.key;
$scope.last_key_code = $event.keyCode;
$scope.first_channel.send('KeyDown', $event);
$scope.$apply();
$event.preventDefault();
};
$document.bind("keydown", $scope.onKeyDown);
// Conext Menu Button Handlers
$scope.removeContextMenu = function(){
let context_menu = $scope.context_menus[0];
context_menu.enabled = false;
context_menu.x = -100000;
context_menu.y = -100000;
context_menu.buttons.forEach(function(button, index){
context_menu.buttons.forEach(function(button){
button.enabled = false;
button.x = -100000;
button.y = -100000;
});
}
};
$scope.closeDetailsPanel = function () {
$scope.$emit('closeDetailsPanel');
};
$scope.onDetailsContextButton = function (panelBoolean) {
if (!$scope.disconnected) {
@@ -689,6 +728,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
index = $scope.devices.indexOf(devices[i]);
if (index !== -1) {
$scope.devices.splice(index, 1);
$scope.$emit('removeSearchOption', devices[i]);
$scope.send_control_message(new messages.DeviceDestroy($scope.client_id,
devices[i].id,
devices[i].x,
@@ -790,29 +830,85 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope[`on${functionName}Button`]();
});
$scope.$on('jumpTo', function(e, zoomLevel){
$scope.$on('unbind', function(){
$scope.first_channel.send('UnbindDocument', {});
});
$scope.$on('bind', function(){
$scope.first_channel.send('BindDocument', {});
});
$scope.jump_to_animation = function(jump_to_x, jump_to_y, jump_to_scale) {
$scope.cancel_animations();
var v_center = $scope.to_virtual_coordinates($scope.graph.width/2, $scope.graph.height/2);
//console.log({v_center: v_center});
$scope.jump.from_x = v_center.x;
$scope.jump.from_y = v_center.y;
$scope.jump.to_x = jump_to_x;
$scope.jump.to_y = jump_to_y;
var distance = util.distance(v_center.x, v_center.y, jump_to_x, jump_to_y);
//console.log({distance: distance});
var num_frames = 30 * Math.floor((1 + 4 * distance / (distance + 3000)));
//console.log({num_frames: num_frames});
var scale_animation = new models.Animation($scope.animation_id_seq(),
num_frames,
{
c: -0.1,
distance: distance,
end_height: (1.0/jump_to_scale) - 1,
current_scale: $scope.current_scale,
scope: $scope
},
$scope,
$scope,
animations.scale_animation);
$scope.animations.push(scale_animation);
var pan_animation = new models.Animation($scope.animation_id_seq(),
num_frames,
{
x2: jump_to_x,
y2: jump_to_y,
x1: v_center.x,
y1: v_center.y,
scope: $scope
},
$scope,
$scope,
animations.pan_animation);
$scope.animations.push(pan_animation);
};
$scope.$on('search', function(e, device){
var num_frames = 30;
var searched;
for(var i = 0; i < $scope.devices.length; i++){
if(Number(device.id) === $scope.devices[i].id){
searched = $scope.devices[i];
}
}
searched.selected = true;
$scope.selected_devices.push(searched);
//console.log(searched);
$scope.jump_to_animation(searched.x, searched.y, 1.0);
});
$scope.$on('jumpTo', function(e, zoomLevel) {
var v_center = $scope.to_virtual_coordinates($scope.graph.width/2, $scope.graph.height/2);
switch (zoomLevel){
case 'site':
$scope.current_scale = 0.051;
$scope.jump_to_animation(v_center.x, v_center.y, 0.051);
break;
case 'rack':
$scope.current_scale = 0.11;
$scope.jump_to_animation(v_center.x, v_center.y, 0.11);
break;
case 'inventory':
$scope.current_scale = 0.51;
$scope.jump_to_animation(v_center.x, v_center.y, 0.51);
break;
case 'process':
$scope.current_scale = 1.1;
$scope.jump_to_animation(v_center.x, v_center.y, 5.1);
break;
}
// var new_panX = controller.scope.{{somethinghere}} - new_scale * ((controller.scope.mouseX - controller.scope.panX) / controller.scope.current_scale);
// var new_panY = controller.scope.mouseY - new_scale * ((controller.scope.mouseY - controller.scope.panY) / controller.scope.current_scale);
// // controller.scope.updateScaledXY();
// // controller.scope.current_scale = new_scale;
// controller.scope.panX = new_panX;
// controller.scope.panY = new_panY;
$scope.updateScaledXY();
$scope.updatePanAndScale();
});
$scope.onDeployButton = function (button) {
@@ -1691,6 +1787,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
}
$scope.updateInterfaceDots();
$scope.$emit('instatiateSelect', $scope.devices);
};
$scope.updateInterfaceDots = function() {
@@ -1774,7 +1871,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.$on('$destroy', function () {
console.log("Network UI stopping");
$document.unbind('keydown', $scope.onKeyDown);
$scope.first_channel.send('UnbindDocument', {});
});
$scope.update_toolbox_heights = function(){
@@ -1877,6 +1974,8 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.null_controller.state.start($scope.null_controller);
$scope.hotkeys_controller.state = hotkeys.Start;
$scope.hotkeys_controller.state.start($scope.hotkeys_controller);
$scope.keybindings_controller.state = keybindings.Start;
$scope.keybindings_controller.state.start($scope.keybindings_controller);
$scope.view_controller.state = view.Start;
$scope.view_controller.state.start($scope.view_controller);
$scope.device_detail_controller.state = device_detail_fsm.Start;
@@ -1927,6 +2026,15 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
$scope.rack_toolbox.items = [];
$scope.site_toolbox.items = [];
};
$scope.cancel_animations = function () {
var i = 0;
for (i = 0; i < $scope.animations.length; i++) {
this.animations[i].fsm.handle_message('AnimationCancelled');
}
$scope.animations = [];
};
};
exports.NetworkUIController = NetworkUIController;