blob: 9cb674c68ac8b4bfab4e711cdaa03dba0d46336e [file] [log] [blame]
Thomas Vachuskae586b792015-03-26 13:59:38 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuskae586b792015-03-26 13:59:38 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 ONOS GUI -- Host View Module
19 */
20
21(function () {
22 'use strict';
23
Simon Hunte408af72017-06-14 18:32:06 -070024 // injected refs
25 var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks;
26
27 // internal state
28 var detailsPanel,
29 pStartY,
30 pHeight,
31 top,
Simon Hunte408af72017-06-14 18:32:06 -070032 iconDiv,
33 wSize,
Steven Burrows1c2a9682017-07-14 16:52:46 +010034 editingName = false;
Simon Hunte408af72017-06-14 18:32:06 -070035
36 // constants
37 var topPdg = 28,
Simon Hunte408af72017-06-14 18:32:06 -070038 pName = 'host-details-panel',
39 detailsReq = 'hostDetailsRequest',
40 detailsResp = 'hostDetailsResponse',
41 nameChangeReq = 'hostNameChangeRequest',
42 nameChangeResp = 'hostNameChangeResponse';
43
Simon Hunt10618f62017-06-15 19:30:52 -070044 var propOrder = [
Steven Burrows1c2a9682017-07-14 16:52:46 +010045 'id', 'ip', 'mac', 'vlan', 'configured', 'location',
Simon Hunt10618f62017-06-15 19:30:52 -070046 ],
47 friendlyProps = [
48 'Host ID', 'IP Address', 'MAC Address', 'VLAN',
Steven Burrows1c2a9682017-07-14 16:52:46 +010049 'Configured', 'Location',
Simon Hunt10618f62017-06-15 19:30:52 -070050 ];
Simon Hunte408af72017-06-14 18:32:06 -070051
52 function closePanel() {
53 if (detailsPanel.isVisible()) {
54 $scope.selId = null;
55 detailsPanel.hide();
56 return true;
57 }
58 return false;
59 }
60
61 function addCloseBtn(div) {
62 is.loadEmbeddedIcon(div, 'close', 20);
63 div.on('click', closePanel);
64 }
65
66 function exitEditMode(nameH2, name) {
67 nameH2.text(name);
68 nameH2.classed('editable clickable', true);
69 editingName = false;
70 ks.enableGlobalKeys(true);
71 }
72
73 function editNameSave() {
74 var nameH2 = top.select('h2'),
75 id = $scope.panelData.id,
Simon Hunt10618f62017-06-15 19:30:52 -070076 ip = $scope.panelData.ip,
Simon Hunte408af72017-06-14 18:32:06 -070077 val,
78 newVal;
79
80 if (editingName) {
81 val = nameH2.select('input').property('value').trim();
Simon Hunt10618f62017-06-15 19:30:52 -070082 newVal = val || ip;
Simon Hunte408af72017-06-14 18:32:06 -070083
84 exitEditMode(nameH2, newVal);
85 $scope.panelData.name = newVal;
86 wss.sendEvent(nameChangeReq, { id: id, name: val });
87 }
88 }
89
90 function editNameCancel() {
91 if (editingName) {
92 exitEditMode(top.select('h2'), $scope.panelData.name);
93 return true;
94 }
95 return false;
96 }
97
98 function editName() {
99 var nameH2 = top.select('h2'),
100 tf, el;
101
102 if (!editingName) {
103 nameH2.classed('editable clickable', false);
104 nameH2.text('');
105 tf = nameH2.append('input').classed('name-input', true)
106 .attr('type', 'text')
107 .attr('value', $scope.panelData.name);
108 el = tf[0][0];
109 el.focus();
110 el.select();
111 editingName = true;
112 ks.enableGlobalKeys(false);
113 }
114 }
115
116 function handleEscape() {
117 return editNameCancel() || closePanel();
118 }
119
120 function setUpPanel() {
Simon Hunt10618f62017-06-15 19:30:52 -0700121 var container, closeBtn;
Simon Hunte408af72017-06-14 18:32:06 -0700122 detailsPanel.empty();
123
124 container = detailsPanel.append('div').classed('container', true);
125
126 top = container.append('div').classed('top', true);
127 closeBtn = top.append('div').classed('close-btn', true);
128 addCloseBtn(closeBtn);
129 iconDiv = top.append('div').classed('host-icon', true);
130 top.append('h2').classed('editable clickable', true).on('click', editName);
131
Simon Hunt10618f62017-06-15 19:30:52 -0700132 top.append('div').classed('top-tables', true);
Simon Hunte408af72017-06-14 18:32:06 -0700133 top.append('hr');
Simon Hunt10618f62017-06-15 19:30:52 -0700134 }
Simon Hunte408af72017-06-14 18:32:06 -0700135
Simon Hunt10618f62017-06-15 19:30:52 -0700136 function addProp(tbody, index, value) {
137 var tr = tbody.append('tr');
138
139 function addCell(cls, txt) {
140 tr.append('td').attr('class', cls).text(txt);
141 }
142 addCell('label', friendlyProps[index] + ' :');
143 addCell('value', value);
Simon Hunte408af72017-06-14 18:32:06 -0700144 }
145
146 function populateTop(details) {
Simon Hunt10618f62017-06-15 19:30:52 -0700147 var tab = top.select('.top-tables').append('tbody');
148
Simon Hunte408af72017-06-14 18:32:06 -0700149 is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
150 top.select('h2').text(details.name);
151
Simon Hunt10618f62017-06-15 19:30:52 -0700152 propOrder.forEach(function (prop, i) {
153 addProp(tab, i, details[prop]);
154 });
Simon Hunte408af72017-06-14 18:32:06 -0700155 }
156
157 function populateDetails(details) {
158 setUpPanel();
159 populateTop(details);
160 detailsPanel.height(pHeight);
Simon Hunt86943082017-06-15 13:18:42 -0700161 // configure width based on content.. for now hardcoded
Simon Hunt10618f62017-06-15 19:30:52 -0700162 detailsPanel.width(400);
Simon Hunte408af72017-06-14 18:32:06 -0700163 }
164
165 function respDetailsCb(data) {
166 $scope.panelData = data.details;
Simon Hunte408af72017-06-14 18:32:06 -0700167 $scope.$apply();
168 }
169
170 function respNameCb(data) {
171 if (data.warn) {
172 $log.warn(data.warn, data.id);
173 top.select('h2').text(data.id);
174 }
175 }
176
177 function createDetailsPane() {
178 detailsPanel = ps.createPanel(pName, {
179 width: wSize.width,
180 margin: 0,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100181 hideMargin: 0,
Simon Hunte408af72017-06-14 18:32:06 -0700182 });
183 detailsPanel.el().style({
184 position: 'absolute',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100185 top: pStartY + 'px',
Simon Hunte408af72017-06-14 18:32:06 -0700186 });
187 $scope.hidePanel = function () { detailsPanel.hide(); };
188 detailsPanel.hide();
189 }
190
191
192 // Defines the Host View controller...
Thomas Vachuskae586b792015-03-26 13:59:38 -0700193 angular.module('ovHost', [])
194 .controller('OvHostCtrl',
Simon Hunte408af72017-06-14 18:32:06 -0700195 ['$log', '$scope',
196 '$location',
197 'TableBuilderService',
198 'FnService', 'MastService', 'PanelService', 'WebSocketService',
199 'IconService', 'NavService', 'KeyService',
Thomas Vachuskae586b792015-03-26 13:59:38 -0700200
Simon Hunte408af72017-06-14 18:32:06 -0700201 function (_$log_, _$scope_, _$location_,
202 tbs,
203 _fs_, _mast_, _ps_, _wss_,
204 _is_, _ns_, _ks_) {
205
206 var params,
207 handlers = {};
208
209 $log = _$log_;
210 $scope = _$scope_;
211 $loc = _$location_;
212 fs = _fs_;
213 mast = _mast_;
214 ps = _ps_;
215 wss = _wss_;
216 is = _is_;
217 ns = _ns_;
218 ks = _ks_;
219
220 params = $loc.search();
221
222 $scope.panelData = {};
223
224 // details panel handlers
225 handlers[detailsResp] = respDetailsCb;
226 handlers[nameChangeResp] = respNameCb;
227 wss.bindHandlers(handlers);
228
229 // query for if a certain host needs to be highlighted
230 if (params.hasOwnProperty('hostId')) {
231 $scope.selId = params['hostId'];
232 wss.sendEvent(detailsReq, { id: $scope.selId });
233 }
234
235 function selCb($event, row) {
236 if ($scope.selId) {
237 wss.sendEvent(detailsReq, { id: row.id });
238 } else {
239 $scope.hidePanel();
240 }
241 $log.debug('Got a click on:', row);
242 }
243
Bri Prebilic Cole864cdd62015-04-02 15:46:47 -0700244 tbs.buildTable({
Bri Prebilic Cole864cdd62015-04-02 15:46:47 -0700245 scope: $scope,
Simon Hunte408af72017-06-14 18:32:06 -0700246 tag: 'host',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100247 selCb: selCb,
Simon Hunte408af72017-06-14 18:32:06 -0700248 });
249
250 $scope.nav = function (path) {
251 if ($scope.selId) {
252 ns.navTo(path, { hostId: $scope.selId });
253 }
254 };
255
256 $scope.$on('$destroy', function () {
257 wss.unbindHandlers(handlers);
Thomas Vachuskae586b792015-03-26 13:59:38 -0700258 });
Bri Prebilic Cole3d4d01c2015-04-30 13:48:36 -0700259
Bri Prebilic Cole19a32dd2015-03-26 18:00:03 -0700260 $log.log('OvHostCtrl has been created');
Simon Hunte408af72017-06-14 18:32:06 -0700261 }])
262
263 .directive('hostDetailsPanel',
264 ['$rootScope', '$window', '$timeout', 'KeyService',
265 function ($rootScope, $window, $timeout, ks) {
266 return function (scope) {
267 var unbindWatch;
268
269 function heightCalc() {
270 pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height')
271 + mast.mastHeight() + topPdg;
272 wSize = fs.windowSize(pStartY);
273 pHeight = wSize.height;
274 }
275
276 function initPanel() {
277 heightCalc();
278 createDetailsPane();
279 }
280
281 // Safari has a bug where it renders the fixed-layout table wrong
282 // if you ask for the window's size too early
283 if (scope.onos.browser === 'safari') {
284 $timeout(initPanel);
285 } else {
286 initPanel();
287 }
288 // create key bindings to handle panel
289 ks.keyBindings({
290 enter: editNameSave,
291 esc: [handleEscape, 'Close the details panel'],
Steven Burrows1c2a9682017-07-14 16:52:46 +0100292 _helpFormat: ['esc'],
Simon Hunte408af72017-06-14 18:32:06 -0700293 });
294 ks.gestureNotes([
295 ['click', 'Select a row to show device details'],
Steven Burrows1c2a9682017-07-14 16:52:46 +0100296 ['scroll down', 'See more devices'],
Simon Hunte408af72017-06-14 18:32:06 -0700297 ]);
298
299 // if the panelData changes
300 scope.$watch('panelData', function () {
301 if (!fs.isEmptyObject(scope.panelData)) {
302 populateDetails(scope.panelData);
303 detailsPanel.show();
304 }
305 });
306
307 // if the window size changes
308 unbindWatch = $rootScope.$watchCollection(
309 function () {
310 return {
311 h: $window.innerHeight,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100312 w: $window.innerWidth,
Simon Hunte408af72017-06-14 18:32:06 -0700313 };
314 }, function () {
315 if (!fs.isEmptyObject(scope.panelData)) {
316 heightCalc();
317 populateDetails(scope.panelData);
318 }
319 }
320 );
321
322 scope.$on('$destroy', function () {
323 unbindWatch();
324 ks.unbindKeys();
325 ps.destroyPanel(pName);
326 });
327 };
328 }]);
Thomas Vachuskae586b792015-03-26 13:59:38 -0700329}());