GUI -- Application View Details Panel -
  - simplified DOM structure
  - refactored code to reduce boilerplate
  - cleaned up CSS

Change-Id: Iff443d7f038f1f770e7b3e9ed383c65b96ba6886
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
index 5edca13..dca882e 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
@@ -58,7 +58,7 @@
     private static final String URL = "url";
     private static final String README = "readme";
     private static final String ROLE = "role";
-    private static final String REQUIRED_APPS = "_required_apps";
+    private static final String REQUIRED_APPS = "required_apps";
     private static final String FEATURES = "features";
     private static final String PERMISSIONS = "permissions";
 
@@ -175,13 +175,13 @@
 
             // process required applications
             ArrayNode requiredApps = arrayNode();
-            app.requiredApps().forEach(s -> requiredApps.add(s));
+            app.requiredApps().forEach(requiredApps::add);
 
             data.set(REQUIRED_APPS, requiredApps);
 
             // process features
             ArrayNode features = arrayNode();
-            app.features().forEach(f -> features.add(f));
+            app.features().forEach(features::add);
 
             data.set(FEATURES, features);
 
diff --git a/web/gui/src/main/webapp/app/view/app/app.css b/web/gui/src/main/webapp/app/view/app/app.css
index 4912809..eab45ef 100644
--- a/web/gui/src/main/webapp/app/view/app/app.css
+++ b/web/gui/src/main/webapp/app/view/app/app.css
@@ -15,7 +15,7 @@
  */
 
 /*
- ONOS GUI -- Host View -- CSS file
+ ONOS GUI -- Applications View -- CSS file
  */
 
 #ov-app h2 {
@@ -75,7 +75,7 @@
 }
 
 #application-details-panel .container {
-    padding: 0 12px;
+    padding: 0 10px;
 }
 
 #application-details-panel .close-btn {
@@ -91,60 +91,48 @@
     fill: #ccc;
 }
 
-#application-details-panel .dev-icon {
+#application-details-panel .app-icon {
     display: inline-block;
     padding: 0 6px 0 0;
     vertical-align: middle;
 }
-.light .dev-icon svg.embeddedIcon .glyph {
-    fill: rgb(0, 172, 229);
-}
-.dark .dev-icon svg.embeddedIcon .glyph {
-    fill: #486D91;
-}
 
 #application-details-panel h2 {
     display: inline-block;
     margin: 8px 0;
+    font-size: 12pt;
 }
 
 #application-details-panel .top div.left {
     float: left;
-    padding: 0 18px 0 0;
+    padding: 0 12px 0 0;
 }
 #application-details-panel .top div.right {
     display: inline-block;
+    overflow: hidden;
+    width: 320px;
 }
 
-#application-details-panel td.label {
+#application-details-panel td.label,
+#application-details-panel .app-url i {
     font-style: italic;
     padding-right: 12px;
     /* works for both light and dark themes ... */
     color: #777;
 }
 
-#application-details-panel .actionBtns div {
-    padding: 12px 6px;
+#application-details-panel td.value-bold {
+    font-weight: bold;
 }
 
-#application-details-panel .top hr {
+#application-details-panel .app-url {
+    padding: 10px 6px 6px;
+}
+
+#application-details-panel hr {
     width: 95%;
-    margin: 0 auto;
+    margin: 10px auto;
 }
-
-.light #application-details-panel hr {
-    opacity: .5;
-    border-color: #FFF;
-}
-.dark #application-details-panel hr {
-    border-color: #666;
-}
-
-#application-details-panel .middle hr {
-    width: 95%;
-    margin: 0 auto;
-}
-
 .light #application-details-panel hr {
     opacity: .5;
     border-color: #FFF;
@@ -155,25 +143,12 @@
 
 #application-details-panel .bottom table {
     border-spacing: 0;
+    width: 100%;
 }
 
-#application-details-panel .bottom th {
-    letter-spacing: 0.02em;
-}
-
-.light #application-details-panel .bottom th {
-    background-color: #CCC;
-    /* default text color */
-}
-.dark #application-details-panel .bottom th {
-    background-color: #131313;
-    color: #ccc;
-}
-
-#application-details-panel .bottom th,
 #application-details-panel .bottom td {
     padding: 6px 12px;
-    text-align: center;
+    text-align: left;
 }
 
 .light #application-details-panel .bottom tr:nth-child(odd) {
diff --git a/web/gui/src/main/webapp/app/view/app/app.js b/web/gui/src/main/webapp/app/view/app/app.js
index 16c1e72..7f620ae 100644
--- a/web/gui/src/main/webapp/app/view/app/app.js
+++ b/web/gui/src/main/webapp/app/view/app/app.js
@@ -22,7 +22,7 @@
     'use strict';
 
     // injected refs
-    var $log, $scope, $loc, fs, ps, wss, is, ns, ks, is;
+    var $log, $scope, wss, fs, ks, ps, is;
 
     // internal state
     var  detailsPanel,
@@ -31,7 +31,6 @@
          top,
          middle,
          bottom,
-         iconDiv,
          wSize = false;
 
     // constants
@@ -39,9 +38,7 @@
         ACTIVE = 'ACTIVE',
         appMgmtReq = 'appManagementRequest',
         topPdg = 50,
-        ctnrPdg = 24,
-        tbWidth = 470,
-        scrollSize = 17,
+        panelWidth = 500,
         pName = 'application-details-panel',
         detailsReq = 'appDetailsRequest',
         detailsResp = 'appDetailsResponse',
@@ -57,8 +54,9 @@
         },
         discouragement = 'Deactivating or uninstalling this component can' +
         ' have serious negative consequences! Do so at your own risk!!',
-        propOrder = ['id', 'state', 'category', 'version', 'origin', 'role', 'url'],
-        friendlyProps = ['App ID', 'State', 'Category', 'Version', 'Origin', 'Role', 'URL'];
+        propOrder = ['id', 'state', 'category', 'version', 'origin', 'role'],
+        friendlyProps = ['App ID', 'State', 'Category', 'Version', 'Origin', 'Role'];
+        // note: url is handled separately
 
     function createDetailsPane() {
         detailsPanel = ps.createPanel(pName, {
@@ -90,158 +88,113 @@
     }
 
     function setUpPanel() {
-        var container, closeBtn, tblDiv;
+        var container, closeBtn, div;
+
         detailsPanel.empty();
+        detailsPanel.width(panelWidth);
 
         container = detailsPanel.append('div').classed('container', true);
 
         top = container.append('div').classed('top', true);
         closeBtn = top.append('div').classed('close-btn', true);
         addCloseBtn(closeBtn);
-        iconDiv = top.append('div').classed('dev-icon', true);
 
-        tblDiv = top.append('div').classed('top-tables', true);
-        tblDiv.append('div').classed('left', true).append('table');
-        tblDiv.append('div').classed('right', true).append('table');
-        tblDiv.append('div').classed('description', true).append('table');
+        div = top.append('div').classed('top-content', true);
 
-        top.append('hr');
+        function ndiv(cls, tcls) {
+            var  d = div.append('div').classed(cls, true);
+            if (tcls) {
+                d.append('table').classed(tcls, true);
+            }
+        }
+
+        ndiv('left app-icon');
+        ndiv('right', 'app-props');
+        ndiv('app-url');
+
+        container.append('hr');
 
         middle = container.append('div').classed('middle', true);
-        tblDiv = middle.append('div').classed('middle-tables', true);
-        tblDiv.append('div').classed('readme', true).append('table');
+        middle.append('div').classed('app-readme', true);
 
-        middle.append('hr');
+        container.append('hr');
 
+        // TODO: make bottom container scrollable
         bottom = container.append('div').classed('bottom', true);
-        tblDiv = bottom.append('div').classed('bottom-tables', true).append('table');
-        tblDiv.append('h2').html('Features');
-        tblDiv.append('div').classed('features', true).append('table');
-        tblDiv.append('h2').html('Required Apps');
-        tblDiv.append('div').classed('required-apps', true).append('table');
-        tblDiv.append('h2').html('Permissions');
-        tblDiv.append('div').classed('permissions', true).append('table');
+
+        function nTable(hdr, cls) {
+            bottom.append('h2').html(hdr);
+            bottom.append('div').classed(cls, true).append('table');
+        }
+
+        nTable('Features', 'features');
+        nTable('Required Apps', 'required-apps');
+        nTable('Permissions', 'permissions');
     }
 
     function addProp(tbody, index, value) {
-        var tr = tbody.append('tr');
+        var tr = tbody.append('tr'),
+            vcls = index ? 'value' : 'value-bold';
 
         function addCell(cls, txt) {
             tr.append('td').attr('class', cls).html(txt);
         }
-        addCell('label', friendlyProps[index] + ' :');
-        addCell('value', value);
+
+        addCell('label', friendlyProps[index] + ':');
+        addCell(vcls, value);
     }
 
-    function addUrl(tbody, index, value) {
-        var href = '<a href="' + value + '" target="_blank">' + value + '</a>';
-        addProp(tbody, index, href);
+    function urlize(u) {
+        return '<i>URL:</i> <a href="' + u + '" target="_blank">' + u + '</a>';
     }
 
-    function addIcon(tbody, value) {
-        var tr = tbody.append('tr');
-        var td = tr.append('td');
-        td.append('img').attr('src', iconUrlPrefix + value + iconUrlSuffix);
+    function addIcon(elem, value) {
+        elem.append('img').attr('src', iconUrlPrefix + value + iconUrlSuffix);
     }
 
-    function addContent(tbody, value) {
-        var tr = tbody.append('tr');
-        tr.append('td').html(value);
-    }
+    function populateTop(details) {
+        var propsBody = top.select('.app-props').append('tbody'),
+            url = details.url;
 
-    function populateTop(tblDiv, details) {
-        var leftTbl = tblDiv.select('.left')
-                        .select('table')
-                        .append('tbody'),
-            rightTbl = tblDiv.select('.right')
-                        .select('table')
-                        .append('tbody'),
-            descriptionTbl = tblDiv.select('.description')
-                        .select('table')
-                        .append('tbody');
+        addIcon(top.select('.app-icon'), details.id);
 
-        top.select('h2').html(details.name);
-
-        // place application icon to the left table
-        addIcon(leftTbl, details.id);
-
-        // place rest of the fields to the right table
         propOrder.forEach(function (prop, i) {
-            var fn = prop === 'url' ? addUrl : addProp;
-            fn(rightTbl, i, details[prop]);
+            addProp(propsBody, i, details[prop]);
         });
 
-        // place description field to the description table
-        addContent(descriptionTbl, details.desc);
+        if (url) {
+            top.select('.app-url').html(urlize(url));
+        }
     }
 
-    function populateMiddle(tblDiv, details) {
-        var readmeTbl = tblDiv.select('.readme')
-                        .select('table')
-                        .append('tbody');
-
-        // place readme field to the readme table
-        addContent(readmeTbl, details.readme);
+    function populateMiddle(details) {
+        middle.select('.app-readme').text(details.readme);
     }
 
-    function populateName(div, name) {
-        var lab = div.select('.label'),
-            val = div.select('.value');
-        lab.html('Friendly Name:');
-        val.html(name);
+    function populateBottom(details) {
+
+        function addItems(cls, items) {
+            var table = bottom.select('.' + cls).select('table'),
+                tbody = table.append('tbody');
+
+            items.forEach(function (item) {
+                tbody.append('tr').append('td').html(item);
+            });
+        }
+
+        addItems('features', details.features);
+        addItems('required-apps', details.required_apps);
+        addItems('permissions', details.permissions);
     }
 
     function populateDetails(details) {
-        var nameDiv, topTbs, middleTbs, bottomTbs;
         setUpPanel();
-
-        nameDiv = top.select('.name-div');
-        topTbs = top.select('.top-tables');
-        middleTbs = middle.select('.middle-tables');
-        bottomTbs = bottom.select('.bottom-tables');
-
-        populateName(nameDiv, details.name);
-        populateTop(topTbs, details);
-        populateMiddle(middleTbs, details);
-        populateBottom(bottomTbs, details);
-
+        populateTop(details);
+        populateMiddle(details);
+        populateBottom(details);
         detailsPanel.height(pHeight);
     }
 
-    function addItem(tbody, item) {
-        var tr = tbody.append('tr').attr('width', tbWidth + 'px');
-        tr.append('td').attr('width', tbWidth + 'px')
-                       .attr('style', 'text-align:left').html(item);
-    }
-
-    function addItems(table, items) {
-        var tbody = table.append('tbody');
-        items.forEach(function (item) {
-            addItem(tbody, item);
-        });
-    }
-
-    function populateBottom(tblDiv, details) {
-        var featuresTbl = tblDiv.select('.features')
-                                    .select('table'),
-            permissionsTbl = tblDiv.select('.permissions')
-                                    .select('table'),
-            requiredAppsTbl = tblDiv.select('.required-apps')
-                                    .select('table');
-
-        addItems(featuresTbl, details.features);
-        addItems(requiredAppsTbl, details._required_apps);
-        addItems(permissionsTbl, details.permissions);
-
-        featuresTbl.style({
-            width: tbWidth + 'px',
-            overflow: 'auto',
-            display: 'block'
-        });
-
-        detailsPanel.width(tbWidth + ctnrPdg);
-    }
-
     function respDetailsCb(data) {
         $scope.panelData = data.details;
         $scope.$apply();
@@ -250,15 +203,15 @@
     angular.module('ovApp', [])
     .controller('OvAppCtrl',
         ['$log', '$scope', '$http',
-        'FnService', 'TableBuilderService', 'PanelService', 'WebSocketService',
-        'IconService', 'UrlFnService', 'KeyService', 'DialogService',
+         'WebSocketService', 'FnService', 'KeyService', 'PanelService',
+         'IconService', 'UrlFnService', 'DialogService', 'TableBuilderService',
 
-    function (_$log_, _$scope_, $http, _fs_, tbs, _ps_, _wss_, _is_, ufs, _ks_, ds) {
+    function (_$log_, _$scope_, $http, _wss_, _fs_, _ks_, _ps_, _is_, ufs, ds, tbs) {
         $log = _$log_;
         $scope = _$scope_;
         wss = _wss_;
-        ks = _ks_;
         fs = _fs_;
+        ks = _ks_;
         ps = _ps_;
         is = _is_;
         $scope.panelData = {};
@@ -285,7 +238,6 @@
             } else {
                 $scope.hidePanel();
             }
-            $log.debug('Got a click on:', row);
         }
 
         function refreshCtrls() {
@@ -318,12 +270,12 @@
 
         // TODO: reexamine where keybindings should be - directive or controller?
         ks.keyBindings({
-            esc: [$scope.selectCallback, 'Deselect app'],
+            esc: [$scope.selectCallback, 'Deselect application'],
             _helpFormat: ['esc']
         });
         ks.gestureNotes([
-            ['click row', 'Select / deselect app'],
-            ['scroll down', 'See more apps']
+            ['click row', 'Select / deselect application'],
+            ['scroll down', 'See more applications']
         ]);
 
         function createConfirmationText(action, itemId) {