Thomas Vachuska

GUI -- Added applications view.

Fixed table.js column width computation.
Fixed app.xml files to leave out ONOS from description.

Change-Id: Icfe323e63c7965dd8c3a268421ea58065c5c8236
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.bgprouter" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.bgprouter" origin="ON.Lab" version="1.2.0"
18 features="onos-app-bgprouter"> 18 features="onos-app-bgprouter">
19 - <description>ONOS BGP router application</description> 19 + <description>BGP router application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.config" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.config" origin="ON.Lab" version="1.2.0"
18 features="onos-app-config"> 18 features="onos-app-config">
19 - <description>ONOS network configuration application</description> 19 + <description>Network configuration application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.fwd" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.fwd" origin="ON.Lab" version="1.2.0"
18 features="onos-app-fwd"> 18 features="onos-app-fwd">
19 - <description>ONOS Reactive forwarding application using flow subsystem</description> 19 + <description>Reactive forwarding application using flow subsystem</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.metrics" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.metrics" origin="ON.Lab" version="1.2.0"
18 features="onos-app-metrics"> 18 features="onos-app-metrics">
19 - <description>ONOS performance metrics collection</description> 19 + <description>Performance metrics collection</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.mobility" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.mobility" origin="ON.Lab" version="1.2.0"
18 features="onos-app-mobility"> 18 features="onos-app-mobility">
19 - <description>ONOS host mobility application</description> 19 + <description>Host mobility application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.optical" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.optical" origin="ON.Lab" version="1.2.0"
18 features="onos-app-optical"> 18 features="onos-app-optical">
19 - <description>ONOS Packet/Optical use-case application</description> 19 + <description>Packet/Optical use-case application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.proxyarp" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.proxyarp" origin="ON.Lab" version="1.2.0"
18 features="onos-app-proxyarp"> 18 features="onos-app-proxyarp">
19 - <description>ONOS proxy ARP/NDP application</description> 19 + <description>Proxy ARP/NDP application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.sdnip" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.sdnip" origin="ON.Lab" version="1.2.0"
18 features="onos-app-sdnip"> 18 features="onos-app-sdnip">
19 - <description>ONOS SDN/IP use-case application</description> 19 + <description>SDN/IP use-case application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.election" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.election" origin="ON.Lab" version="1.2.0"
18 features="onos-app-election"> 18 features="onos-app-election">
19 - <description>ONOS master election test application</description> 19 + <description>Master election test application</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.intentperf" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.intentperf" origin="ON.Lab" version="1.2.0"
18 features="onos-app-intent-perf"> 18 features="onos-app-intent-perf">
19 - <description>Intent performance application</description> 19 + <description>Intent performance test application</description>
20 </app> 20 </app>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.null" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.null" origin="ON.Lab" version="1.2.0"
18 features="onos-null"> 18 features="onos-null">
19 - <description>ONOS null southbound providers for testing</description> 19 + <description>Null southbound providers for testing</description>
20 </app> 20 </app>
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 --> 16 -->
17 <app name="org.onosproject.openflow" origin="ON.Lab" version="1.2.0" 17 <app name="org.onosproject.openflow" origin="ON.Lab" version="1.2.0"
18 features="onos-openflow"> 18 features="onos-openflow">
19 - <description>ONOS OpenFlow protocol southbound providers</description> 19 + <description>OpenFlow protocol southbound providers</description>
20 </app> 20 </app>
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
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 +package org.onosproject.ui.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ArrayNode;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import com.google.common.collect.ImmutableSet;
21 +import org.onosproject.app.ApplicationService;
22 +import org.onosproject.app.ApplicationState;
23 +import org.onosproject.core.Application;
24 +
25 +import java.util.Arrays;
26 +import java.util.List;
27 +import java.util.stream.Collectors;
28 +
29 +import static org.onosproject.app.ApplicationState.ACTIVE;
30 +
31 +/**
32 + * Message handler for application view related messages.
33 + */
34 +public class ApplicationViewMessageHandler extends AbstractTabularViewMessageHandler {
35 +
36 + /**
37 + * Creates a new message handler for the application messages.
38 + */
39 + protected ApplicationViewMessageHandler() {
40 + super(ImmutableSet.of("appDataRequest"));
41 + }
42 +
43 + @Override
44 + public void process(ObjectNode message) {
45 + ObjectNode payload = payload(message);
46 + String sortCol = string(payload, "sortCol", "id");
47 + String sortDir = string(payload, "sortDir", "asc");
48 +
49 + ApplicationService service = get(ApplicationService.class);
50 + TableRow[] rows = generateTableRows(service);
51 + RowComparator rc =
52 + new RowComparator(sortCol, RowComparator.direction(sortDir));
53 + Arrays.sort(rows, rc);
54 + ArrayNode applications = generateArrayNode(rows);
55 + ObjectNode rootNode = mapper.createObjectNode();
56 + rootNode.set("applications", applications);
57 +
58 + connection().sendMessage("appDataResponse", 0, rootNode);
59 + }
60 +
61 + private TableRow[] generateTableRows(ApplicationService service) {
62 + List<TableRow> list = service.getApplications().stream()
63 + .map(application -> new ApplicationTableRow(service, application))
64 + .collect(Collectors.toList());
65 + return list.toArray(new TableRow[list.size()]);
66 + }
67 +
68 + /**
69 + * TableRow implementation for {@link org.onosproject.core.Application applications}.
70 + */
71 + private static class ApplicationTableRow extends AbstractTableRow {
72 +
73 + private static final String STATE = "state";
74 + private static final String STATE_IID = "_iconid_state";
75 + private static final String ID = "id";
76 + private static final String VERSION = "version";
77 + private static final String ORIGIN = "origin";
78 + private static final String DESC = "desc";
79 +
80 + private static final String[] COL_IDS = {
81 + STATE, STATE_IID, ID, VERSION, ORIGIN, DESC
82 + };
83 +
84 + private static final String ICON_ID_ACTIVE = "appActive";
85 + private static final String ICON_ID_INACTIVE = "appInactive";
86 +
87 +
88 + public ApplicationTableRow(ApplicationService service, Application app) {
89 + ApplicationState state = service.getState(app.id());
90 + String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE;
91 +
92 + add(STATE, state.toString());
93 + add(STATE_IID, iconId);
94 + add(ID, app.id().name());
95 + add(VERSION, app.version().toString());
96 + add(ORIGIN, app.origin());
97 + add(DESC, app.description());
98 + }
99 +
100 + @Override
101 + protected String[] columnIds() {
102 + return COL_IDS;
103 + }
104 + }
105 +
106 +}
...@@ -59,12 +59,14 @@ public class UiExtensionManager implements UiExtensionService { ...@@ -59,12 +59,14 @@ public class UiExtensionManager implements UiExtensionService {
59 List<UiView> coreViews = of(new UiView("topo", "Topology View"), 59 List<UiView> coreViews = of(new UiView("topo", "Topology View"),
60 new UiView("device", "Devices"), 60 new UiView("device", "Devices"),
61 new UiView("host", "Hosts"), 61 new UiView("host", "Hosts"),
62 + new UiView("app", "Applications"),
62 new UiView("sample", "Sample")); 63 new UiView("sample", "Sample"));
63 UiMessageHandlerFactory messageHandlerFactory = 64 UiMessageHandlerFactory messageHandlerFactory =
64 () -> ImmutableList.of( 65 () -> ImmutableList.of(
65 new TopologyViewMessageHandler(), 66 new TopologyViewMessageHandler(),
66 new DeviceViewMessageHandler(), 67 new DeviceViewMessageHandler(),
67 - new HostViewMessageHandler() 68 + new HostViewMessageHandler(),
69 + new ApplicationViewMessageHandler()
68 ); 70 );
69 return new UiExtension(coreViews, messageHandlerFactory, "core", 71 return new UiExtension(coreViews, messageHandlerFactory, "core",
70 UiExtensionManager.class.getClassLoader()); 72 UiExtensionManager.class.getClassLoader());
......
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
2 <link rel="stylesheet" href="app/view/topo/topo.css"> 2 <link rel="stylesheet" href="app/view/topo/topo.css">
3 <link rel="stylesheet" href="app/view/device/device.css"> 3 <link rel="stylesheet" href="app/view/device/device.css">
4 <link rel="stylesheet" href="app/view/host/host.css"> 4 <link rel="stylesheet" href="app/view/host/host.css">
5 +<link rel="stylesheet" href="app/view/app/app.css">
......
...@@ -13,4 +13,5 @@ ...@@ -13,4 +13,5 @@
13 <script src="app/view/topo/topoToolbar.js"></script> 13 <script src="app/view/topo/topoToolbar.js"></script>
14 <script src="app/view/device/device.js"></script> 14 <script src="app/view/device/device.js"></script>
15 <script src="app/view/host/host.js"></script> 15 <script src="app/view/host/host.js"></script>
16 +<script src="app/view/app/app.js"></script>
16 <script src="app/view/sample/sample.js"></script> 17 <script src="app/view/sample/sample.js"></script>
......
...@@ -43,10 +43,21 @@ table.summary-list tbody { ...@@ -43,10 +43,21 @@ table.summary-list tbody {
43 background-color: #444; 43 background-color: #444;
44 } 44 }
45 45
46 +.light table.summary-list tr.selected {
47 + background-color: deepskyblue;
48 +}
49 +
50 +.dark table.summary-list tr.selected {
51 + background-color: #304860;
52 +}
53 +
54 +table.summary-list td,th {
55 + padding: 6px 6px 6px 6px;
56 + text-align: left;
57 +}
58 +
46 table.summary-list th { 59 table.summary-list th {
47 - padding: 10px;
48 letter-spacing: 0.02em; 60 letter-spacing: 0.02em;
49 - text-align: left;
50 cursor: pointer; 61 cursor: pointer;
51 } 62 }
52 table.summary-list th:first-child { 63 table.summary-list th:first-child {
...@@ -55,6 +66,7 @@ table.summary-list th:first-child { ...@@ -55,6 +66,7 @@ table.summary-list th:first-child {
55 table.summary-list th:last-child { 66 table.summary-list th:last-child {
56 border-radius: 0 8px 0 0; 67 border-radius: 0 8px 0 0;
57 } 68 }
69 +
58 .light table.summary-list th { 70 .light table.summary-list th {
59 background-color: #bbb; 71 background-color: #bbb;
60 } 72 }
...@@ -63,11 +75,6 @@ table.summary-list th:last-child { ...@@ -63,11 +75,6 @@ table.summary-list th:last-child {
63 color: #ccc; 75 color: #ccc;
64 } 76 }
65 77
66 -table.summary-list td {
67 - padding: 6px 10px 6px 10px;
68 - text-align: left;
69 -}
70 -
71 .dark table.summary-list td { 78 .dark table.summary-list td {
72 color: #ccc; 79 color: #ccc;
73 } 80 }
......
...@@ -28,6 +28,29 @@ svg.embeddedIcon .icon .glyph { ...@@ -28,6 +28,29 @@ svg.embeddedIcon .icon .glyph {
28 fill-rule: evenodd; 28 fill-rule: evenodd;
29 } 29 }
30 30
31 +/*
32 + FIXME: The following should be consolidated to result in much less CSS and
33 + not to require entries for every icon.
34 + */
35 +
36 +.light svg.embeddedIcon .icon.appActive,
37 +.light svg.embeddedIcon .icon.appInactive {
38 + fill: none;
39 +}
40 +
41 +.dark svg.embeddedIcon .icon.appActive,
42 +.dark svg.embeddedIcon .icon.appInactive {
43 + fill: none;
44 +}
45 +
46 +.light svg.embeddedIcon .icon.appActive .glyph {
47 + fill: green;
48 +}
49 +.dark svg.embeddedIcon .icon.appActive .glyph {
50 + fill: #266610;
51 +}
52 +
53 +
31 .light svg.embeddedIcon .icon.deviceOnline, 54 .light svg.embeddedIcon .icon.deviceOnline,
32 .light svg.embeddedIcon .icon.deviceOffline { 55 .light svg.embeddedIcon .icon.deviceOffline {
33 fill: none; 56 fill: none;
...@@ -73,6 +96,10 @@ svg.embeddedIcon .icon .glyph { ...@@ -73,6 +96,10 @@ svg.embeddedIcon .icon .glyph {
73 fill: #ccc; 96 fill: #ccc;
74 } 97 }
75 98
99 +.light svg.embeddedIcon .icon.appActive rect,
100 +.light svg.embeddedIcon .icon.appInactive rect,
101 +.dark svg.embeddedIcon .icon.appActive rect,
102 +.dark svg.embeddedIcon .icon.appInactive rect,
76 .light svg.embeddedIcon .icon.deviceOnline rect, 103 .light svg.embeddedIcon .icon.deviceOnline rect,
77 .light svg.embeddedIcon .icon.deviceOffline rect, 104 .light svg.embeddedIcon .icon.deviceOffline rect,
78 .dark svg.embeddedIcon .icon.deviceOnline rect, 105 .dark svg.embeddedIcon .icon.deviceOnline rect,
......
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
29 // Maps icon ID to the glyph ID it uses. 29 // Maps icon ID to the glyph ID it uses.
30 // NOTE: icon ID maps to a CSS class for styling that icon 30 // NOTE: icon ID maps to a CSS class for styling that icon
31 var glyphMapping = { 31 var glyphMapping = {
32 + appActive: 'checkMark',
33 + appInactive: 'unknown',
34 +
32 deviceOnline: 'checkMark', 35 deviceOnline: 'checkMark',
33 deviceOffline: 'xMark', 36 deviceOffline: 'xMark',
34 devIcon_SWITCH: 'switch', 37 devIcon_SWITCH: 'switch',
......
...@@ -29,19 +29,31 @@ ...@@ -29,19 +29,31 @@
29 // Functions for creating a fixed header on a table (Angular Directive) 29 // Functions for creating a fixed header on a table (Angular Directive)
30 30
31 function setTableWidth(t) { 31 function setTableWidth(t) {
32 - var tHeaders, tdElement, colWidth, numCols, 32 + var tHeaders, tdElement, colWidth, numIcons, numNonIcons,
33 winWidth = fs.windowSize().width; 33 winWidth = fs.windowSize().width;
34 34
35 tHeaders = t.selectAll('th'); 35 tHeaders = t.selectAll('th');
36 - numCols = tHeaders[0].length; 36 + numIcons = 0;
37 - colWidth = Math.floor(winWidth / numCols); 37 + numNonIcons = 0;
38 +
39 + // FIXME: This should observe custom-set width from the HTML
38 40
39 tHeaders.each(function(thElement, index) { 41 tHeaders.each(function(thElement, index) {
40 thElement = d3.select(this); 42 thElement = d3.select(this);
43 + if (thElement.classed('table-icon')) {
44 + numIcons = numIcons + 1;
45 + } else {
46 + numNonIcons = numNonIcons + 1;
47 + }
48 + });
41 49
50 + colWidth = Math.floor((winWidth - (numIcons * tableIconTdSize)) / numNonIcons);
51 +
52 + tHeaders.each(function(thElement, index) {
53 + thElement = d3.select(this);
42 tdElement = t.select('td:nth-of-type(' + (index + 1) + ')'); 54 tdElement = t.select('td:nth-of-type(' + (index + 1) + ')');
43 55
44 - if (tdElement.classed('table-icon')) { 56 + if (thElement.classed('table-icon')) {
45 thElement.style('width', tableIconTdSize + 'px'); 57 thElement.style('width', tableIconTdSize + 'px');
46 tdElement.style('width', tableIconTdSize + 'px'); 58 tdElement.style('width', tableIconTdSize + 'px');
47 } else { 59 } else {
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
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 -- CSS file
19 + */
20 +
21 +#ov-app td {
22 +}
1 +<!-- app partial HTML -->
2 +<div id="ov-app">
3 + <h2>Applications ({{ctrl.appData.length}} total)</h2>
4 + <table class="summary-list"
5 + onos-fixed-header
6 + onos-sortable-header
7 + sort-callback="sortCallback(requestParams)">
8 + <thead>
9 + <tr>
10 + <th colId="state" class="table-icon" sortable></th>
11 + <th colId="id" sortable>App ID </th>
12 + <th colId="version" sortable>Version</th>
13 + <th colId="origin" sortable>Origin </th>
14 + <th colId="desc">Description </th>
15 + </tr>
16 + </thead>
17 +
18 + <tbody>
19 + <tr ng-repeat="app in ctrl.appData"
20 + ng-click="setSelected(app.id)"
21 + ng-class="{selected: app.id === selectedAppId}"
22 + ng-repeat-done>
23 + <td class="table-icon">
24 + <div icon icon-id="{{app._iconid_state}}"></div>
25 + </td>
26 + <td>{{app.id}}</td>
27 + <td>{{app.version}}</td>
28 + <td>{{app.origin}}</td>
29 + <td>{{app.desc}}</td>
30 + </tr>
31 + </tbody>
32 + </table>
33 +
34 +</div>
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
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 -- App View Module
19 + */
20 +
21 +(function () {
22 + 'use strict';
23 +
24 + angular.module('ovApp', [])
25 + .controller('OvAppCtrl',
26 + ['$log', '$scope', '$location', 'FnService', 'WebSocketService',
27 +
28 + function ($log, $scope, $location, fs, wss) {
29 + var self = this;
30 + self.appData = [];
31 +
32 + $scope.responseCallback = function(data) {
33 + self.appData = data.applications;
34 + $scope.$apply();
35 + };
36 +
37 + $scope.sortCallback = function (requestParams) {
38 + wss.sendEvent('appDataRequest', requestParams);
39 + };
40 +
41 + $scope.selectedAppId = null;
42 + $scope.setSelected = function (appId) {
43 + $scope.selectedAppId = appId;
44 + };
45 +
46 + var handlers = {
47 + appDataResponse: $scope.responseCallback
48 + };
49 + wss.bindHandlers(handlers);
50 +
51 + // Cleanup on destroyed scope
52 + $scope.$on('$destroy', function () {
53 + wss.unbindHandlers(handlers);
54 + });
55 +
56 + $scope.sortCallback();
57 +
58 + $log.log('OvAppCtrl has been created');
59 + }]);
60 +}());
...@@ -18,6 +18,5 @@ ...@@ -18,6 +18,5 @@
18 ONOS GUI -- Device View -- CSS file 18 ONOS GUI -- Device View -- CSS file
19 */ 19 */
20 20
21 -#ov-device { 21 +#ov-device td {
22 - /* placeholder */
23 } 22 }
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
7 sort-callback="sortCallback(requestParams)"> 7 sort-callback="sortCallback(requestParams)">
8 <thead> 8 <thead>
9 <tr> 9 <tr>
10 - <th colId="available"></th> 10 + <th colId="available" class="table-icon" sortable></th>
11 - <th colId="type"></th> 11 + <th colId="type" class="table-icon" sortable></th>
12 <th colId="id" sortable>Device ID </th> 12 <th colId="id" sortable>Device ID </th>
13 <th colId="mfr" sortable>Vendor </th> 13 <th colId="mfr" sortable>Vendor </th>
14 <th colId="hw" sortable>H/W Version </th> 14 <th colId="hw" sortable>H/W Version </th>
......
...@@ -19,5 +19,5 @@ ...@@ -19,5 +19,5 @@
19 */ 19 */
20 20
21 #ov-host td { 21 #ov-host td {
22 - padding: 12px 10px; 22 + height: 20px;
23 } 23 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
101 <script src="app/view/topo/topoToolbar.js"></script> 101 <script src="app/view/topo/topoToolbar.js"></script>
102 <script src="app/view/device/device.js"></script> 102 <script src="app/view/device/device.js"></script>
103 <script src="app/view/host/host.js"></script> 103 <script src="app/view/host/host.js"></script>
104 + <script src="app/view/app/app.js"></script>
104 <script src="app/view/sample/sample.js"></script> 105 <script src="app/view/sample/sample.js"></script>
105 <!-- {INJECTED-JAVASCRIPT-END} --> 106 <!-- {INJECTED-JAVASCRIPT-END} -->
106 107
...@@ -110,6 +111,7 @@ ...@@ -110,6 +111,7 @@
110 <link rel="stylesheet" href="app/view/topo/topo.css"> 111 <link rel="stylesheet" href="app/view/topo/topo.css">
111 <link rel="stylesheet" href="app/view/device/device.css"> 112 <link rel="stylesheet" href="app/view/device/device.css">
112 <link rel="stylesheet" href="app/view/host/host.css"> 113 <link rel="stylesheet" href="app/view/host/host.css">
114 + <link rel="stylesheet" href="app/view/app/app.css">
113 <link rel="stylesheet" href="app/view/sample/sample.css"> 115 <link rel="stylesheet" href="app/view/sample/sample.css">
114 <!-- {INJECTED-STYLESHEETS-END} --> 116 <!-- {INJECTED-STYLESHEETS-END} -->
115 117
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
39 'topo', 39 'topo',
40 'device', 40 'device',
41 'host', 41 'host',
42 + 'app',
42 'sample', 43 'sample',
43 // {INJECTED-VIEW-IDS-END} 44 // {INJECTED-VIEW-IDS-END}
44 45
......