brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [2/7] incubator-brooklyn git commit: Show group members as ‘indirect’ subordinates in tree.
Date Thu, 08 Oct 2015 11:28:32 GMT
Show group members as ‘indirect’ subordinates in tree.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/345daaa0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/345daaa0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/345daaa0

Branch: refs/heads/master
Commit: 345daaa0050b1dbe2fea536a566f6552227294fc
Parents: 99c0119
Author: Alasdair Hodge <github@alasdairhodge.co.uk>
Authored: Thu Oct 1 08:49:51 2015 +0100
Committer: Alasdair Hodge <github@alasdairhodge.co.uk>
Committed: Wed Oct 7 14:33:57 2015 +0100

----------------------------------------------------------------------
 usage/jsgui/src/main/webapp/assets/css/base.css |   9 +
 .../assets/js/view/application-explorer.js      |   2 +-
 .../webapp/assets/js/view/application-tree.js   | 506 +++++++++----------
 .../main/webapp/assets/tpl/apps/tree-item.html  |  12 +-
 4 files changed, 246 insertions(+), 283 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/345daaa0/usage/jsgui/src/main/webapp/assets/css/base.css
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/css/base.css b/usage/jsgui/src/main/webapp/assets/css/base.css
index 04a488d..a80f35b 100644
--- a/usage/jsgui/src/main/webapp/assets/css/base.css
+++ b/usage/jsgui/src/main/webapp/assets/css/base.css
@@ -580,6 +580,15 @@ ol.tree {
 .entity_tree_node_wrapper.active .entity_tree_node {
     font-weight: bold;
 }
+.entity_tree_node_wrapper .indirection-icon {
+    opacity: 0.7;
+    margin-left: -5px;
+}
+.tree-box.indirect > .entity_tree_node_wrapper a {
+    font-style: italic !important;
+    color: #666;
+}
+
 #tree label {
 /* remove the folder, and align with + - icons */
 background: none;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/345daaa0/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js b/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
index b7d6f70..45160cb 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
@@ -77,7 +77,7 @@ define([
                 }
                 this.preselectTab(tab, tabDetails);
             }
-            this.treeView.displayEntityId(entityId)
+            this.treeView.selectEntity(entityId)
         },
         preselectTab: function(tab, tabDetails) {
             this.treeView.preselectTab(tab, tabDetails)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/345daaa0/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js b/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
index 8a5175a..be21e40 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
@@ -31,22 +31,147 @@ define([
     var treeViewTemplate = _.template(TreeItemHtml);
     var notFoundTemplate = _.template(EntityNotFoundHtml);
 
+    var findAllTreeboxes = function(id, $scope) {
+        return $('.tree-box[data-entity-id="' + id + '"]', $scope);
+    };
+
+    var findRootTreebox = function(id) {
+        return $('.lozenge-app-tree-wrapper').children('.tree-box[data-entity-id="' + id
+ '"]', this.$el);
+    };
+
+    var findChildTreebox = function(id, $parentTreebox) {
+        return $parentTreebox.children('.node-children').children('.tree-box[data-entity-id="'
+ id + '"]');
+    };
+
+    var findMasterTreebox = function(id) {
+        return $('.tree-box[data-entity-id="' + id + '"]:not(.indirect)');
+    };
+
+    var createEntityTreebox = function(id, name, $domParent, depth, indirect) {
+        // Tildes in sort key force entities with no name to bottom of list (z < ~).
+        var sortKey = (name ? name.toLowerCase() : "~~~") + "     " + id.toLowerCase();
+
+        // Create the wrapper.
+        var $treebox = $(
+                '<div data-entity-id="'+id+'" data-sort-key="'+sortKey+'" data-depth="'+depth+'"
' +
+                'class="tree-box toggler-group' +
+                    (indirect ? " indirect" : "") +
+                    (depth == 0 ? " outer" : " inner " + (depth % 2 ? " depth-odd" : " depth-even")+
+                    (depth == 1 ? " depth-first" : "")) + '">'+
+                '<div class="entity_tree_node_wrapper"></div>'+
+                '<div class="node-children toggler-target hide"></div>'+
+                '</div>');
+
+        // Insert into the passed DOM parent, maintaining sort order relative to siblings:
name then id.
+        var placed = false;
+        var contender = $(".toggler-group", $domParent).first();
+        while (contender.length && !placed) {
+            var contenderKey = contender.data("sort-key");
+            if (sortKey < contenderKey) {
+                contender.before($treebox);
+                placed = true;
+            } else {
+                contender = contender.next(".toggler-group", $domParent);
+            }
+        }
+        if (!placed) {
+            $domParent.append($treebox);
+        }
+        return $treebox;
+    };
+
+    var getOrCreateApplicationTreebox = function(id, name, treeView) {
+        var $treebox = findRootTreebox(id);
+        if (!$treebox.length) {
+            var $insertionPoint = $('.lozenge-app-tree-wrapper', treeView.$el);
+            if (!$insertionPoint.length) {
+                // entire view must be created
+                treeView.$el.html(
+                        '<div class="navbar_main_wrapper treeloz">'+
+                        '<div id="tree-list" class="navbar_main treeloz">'+
+                        '<div class="lozenge-app-tree-wrapper">'+
+                        '</div></div></div>');
+                $insertionPoint = $('.lozenge-app-tree-wrapper', treeView.$el);
+            }
+            $treebox = createEntityTreebox(id, name, $insertionPoint, 0, false);
+        }
+        return $treebox;
+    };
+
+    var getOrCreateChildTreebox = function(id, name, isIndirect, $parentTreebox) {
+        var $treebox = findChildTreebox(id, $parentTreebox);
+        if (!$treebox.length) {
+            $treebox = createEntityTreebox(id, name, $parentTreebox.children('.node-children'),
$parentTreebox.data("depth") + 1, isIndirect);
+        }
+        return $treebox;
+    };
+
+    var updateTreeboxContent = function(entity, $treebox, treeView) {
+        var $newContent = $(treeView.template({
+            id: entity.get('id'),
+            parentId:  entity.get('parentId'),
+            model: entity,
+            statusIconUrl: ViewUtils.computeStatusIconInfo(entity.get("serviceUp"), entity.get("serviceState")).url,
+            indirect: $treebox.hasClass('indirect'),
+        }));
+
+        var $wrapper = $treebox.children('.entity_tree_node_wrapper');
+
+        // Preserve old display status (just chevron direction at present).
+        if ($wrapper.find('.tree-node-state').hasClass('icon-chevron-down')) {
+            $newContent.find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down');
+        }
+
+        $wrapper.html($newContent);
+        addEventsToNode($treebox, treeView);
+    };
+
+    var addEventsToNode = function($node, treeView) {
+        // show the "light-popup" (expand / expand all / etc) menu
+        // if user hovers for 500ms. surprising there is no option for this (hover delay).
+        // also, annoyingly, clicks around the time the animation starts don't seem to get
handled
+        // if the click is in an overlapping reason; this is why we position relative top:
12px in css
+        $('.light-popup', $node).parent().parent().hover(
+                function(parent) {
+                    treeView.cancelHoverTimer();
+                    treeView.hoverTimer = setTimeout(function() {
+                        var menu = $(parent.currentTarget).find('.light-popup');
+                        menu.show();
+                    }, 500);
+                },
+                function(parent) {
+                    treeView.cancelHoverTimer();
+                    $('.light-popup').hide();
+                }
+        );
+    };
 
-    var ApplicationTreeView = Backbone.View.extend({
+    var selectTreebox = function(id, $treebox, treeView) {
+        $('.entity_tree_node_wrapper').removeClass('active');
+        $treebox.children('.entity_tree_node_wrapper').addClass('active');
+
+        var entity = treeView.collection.get(id);
+        if (entity) {
+            treeView.selectedEntityId = id;
+            treeView.displayEntityId(entity.id, entity.get('applicationId'), false);
+        }
+    };
+
+
+    return Backbone.View.extend({
         template: treeViewTemplate,
         hoverTimer: null,
 
         events: {
-            'click span.entity_tree_node .tree-change':'treeChange',
-            'click span.entity_tree_node':'displayEntity'
+            'click span.entity_tree_node .tree-change': 'treeChange',
+            'click span.entity_tree_node': 'nodeClicked'
         },
-            this.collection.on('all', this.modelEvent, this)
-            this.collection.on('change', this.modelChange, this)
-            this.collection.on('remove', this.modelRemove, this)
-            this.collection.on('add', this.modelAdd, this)
-            this.collection.on('reset', this.renderFull, this)
 
         initialize: function() {
+            this.collection.on('add', this.entityAdded, this);
+            this.collection.on('change', this.entityChanged, this);
+            this.collection.on('remove', this.entityRemoved, this);
+            this.collection.on('reset', this.renderFull, this);
             _.bindAll(this);
         },
 
@@ -54,171 +179,48 @@ define([
             this.collection.off("reset", this.renderFull);
             if (this.detailsView) this.detailsView.close();
         },
-        
-        modelChange: function (child) {
-            this.updateNode(child.id)
-        },
-        modelAdd: function (child) {
-            this.updateNode(child.id)
-        },
-        modelRemove: function (child) {
-            this.removeNode(child.id)
-        },
 
-        modelEvent: function(eventName, event, x) {
-            if (/^change/i.test(eventName) || eventName == "remove" || eventName == "add"
||
-                    eventName == "reset" ||
-                    // above are handled; below is no-op
-                    eventName == "sync" || eventName == "request")
-                return;
+        entityAdded: function(entity) {
+            // Called when the full entity model is fetched into our collection, at which
time we can replace
+            // the empty contents of any placeholder tree nodes (.tree-box) that were created
earlier.
+            // The entity may have multiple 'treebox' views (in the case of group members).
 
-            if (eventName == "error") {
-                log("model error in application-tree - has the internet vanished?")
-                // ignore; app-explorer should clear the view
-                return;
+            // If the new entity is an application, we must create its placeholder in the
DOM.
+            if (!entity.get('parentId')) {
+                getOrCreateApplicationTreebox(entity.id, entity.get('name'), this);
             }
 
-            // don't think we get other events, but just in case:
-            log("unhandled model event");
-            log(eventName);
-            log(event);
-            log(x);
+            this.entityChanged(entity);
         },
 
-        removeNode: function(id) {
-            $('#'+id, this.$el).parent().remove()
-            // collection seems sometimes to have children nodes;
-            // not sure why, but that's okay for now
-            if (this.collection.getApplications().length==0)
-                this.renderFull();
-        },
-        
-        updateNode: function(id, parentId, isApp) {
+        entityChanged: function(entity) {
+            // The entity may have multiple 'treebox' views (in the case of group members).
             var that = this;
-            var nModel = that.collection.get(id);
-            var node = $('#'+id, that.$el)
-            
-            if (!isApp) {
-                // autodiscover whether this is an app, looking at the model and the tree
-                // (at least one should be available -- probably always the former, but...)
-                if (nModel) { isApp = (id == nModel.get('applicationId')); }
-                else if (!isApp && node && node.parent().data('depth')==0)
isApp = true;
-            }
+            findAllTreeboxes(entity.id).each(function() {
+                var $treebox = $(this);
+                updateTreeboxContent(entity, $treebox, that);
+            });
+        },
 
-            if (!isApp && !parentId && nModel)
-                parentId = nModel.get('parentId');
-            if (!isApp && !parentId && node)
-                parentId = node.closest("entity_tree_node_wrapper").data('parentId');
-            if (!isApp && !parentId) {
-                log("no parentId yet available for "+id+"; skipping;")
-                return false;
-            }
-            
-            var statusIconUrl = nModel
-                ? ViewUtils.computeStatusIconInfo(nModel.get("serviceUp"),nModel.get("serviceState")).url
-                : null;
-
-            var newNode = this.template({
-                id:id,
-                parentId:parentId,
-                model:nModel,
-                statusIconUrl:statusIconUrl
-            })
-
-            if (!node.length) {
-                // node does not exist, so add it
-                var parentsChildren, depth;
-                
-                if (isApp) {
-                    parentsChildren = $('.lozenge-app-tree-wrapper', that.$el);
-                    if (!parentsChildren.length) {
-                        // entire view must be created
-                        that.$el.html(
-                                '<div class="navbar_main_wrapper treeloz">'+
-                                '<div id="tree-list" class="navbar_main treeloz">'+
-                                '<div class="lozenge-app-tree-wrapper">'+
-                                '</div></div></div>');
-                        parentsChildren = $('.lozenge-app-tree-wrapper', that.$el);
-                    }
-                    depth = 0;
-                } else {
-                    var parent = $('#'+parentId, that.$el)
-                    if (!parent.length) {
-                        // see if we can load the parent
-                        if (this.updateNode(parentId)) {
-                            parent = $('#'+parentId, that.$el);
-                            if (!parent.length) {
-                                log("no parent element yet available for "+id+" ("+parentId+")
after parent load; skipping")
-                                return false;                                
-                            }
-                        } else {
-                            log("no parent element yet available for "+id+" ("+parentId+");
skipping")
-                            return false;
-                        }
-                    }
-                    parentsChildren = $(parent.parent().children('.node-children'));
-                    depth = parent.parent().data("depth")+1
-                }
+        entityRemoved: function(entity) {
+            // The entity may have multiple 'treebox' views (in the case of group members).
+            findAllTreeboxes(entity.id, this.$el).remove();
+            // Collection seems sometimes to retain children of the removed node;
+            // not sure why, but that's okay for now.
+            if (this.collection.getApplications().length == 0)
+                this.renderFull();
+        },
 
-                // add it, with surrounding html, in parent's node-children child
-                // tildes in sortKey force entities with no name to bottom of list (z <
~).
-                var entityName = nModel && nModel.get("name")
-                        ? nModel.get("name")
-                        : this.collection.getEntityNameFromId(id);
-                var sortKey = (entityName ? entityName.toLowerCase() : "~~~") + "     " +
id.toLowerCase();
-                var newNodeWrapper = $(
-                        '<div data-sort-key="'+sortKey+'" class="toggler-group tree-box
'+
-                            (depth==0 ? "outer" : "inner "+(depth%2==1 ? "depth-odd" : "depth-even")+
-                                (depth==1 ? " depth-first" : "")) + '" data-depth="'+depth+'">'+
-                        '<div id="'+id+'" class="entity_tree_node_wrapper"></div>'+
-                        '<div class="toggler-target hide node-children"></div>'+
-                        '</div>')
-                $('#'+id, newNodeWrapper).html(newNode);
-
-                // Maintain entities sorted by name, then id.
-                var placed = false;
-                var contender = $(".toggler-group", parentsChildren).first();
-                while (contender.length && !placed) {
-                    var contenderKey = contender.data("sort-key");
-                    if (sortKey < contenderKey) {
-                        contender.before(newNodeWrapper);
-                        placed = true;
-                    } else {
-                        contender = contender.next(".toggler-group", parentsChildren);
-                    }
-                }
-                if (!placed) {
-                    parentsChildren.append(newNodeWrapper);
-                }
-                this.addEventsToNode(parentsChildren)
-            } else {
-                // updating
-                var $node = $(node),
-                    $newNode = $(newNode);
-                
-                // preserve old display status (just chevron direction at present)
-                if ($node.find('.tree-node-state').hasClass('icon-chevron-down')) {
-                    $newNode.find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down')
-                    // and if visible, see if any children have been added
-                    var children = nModel.get("children");
-                    var newChildren = []
-                    _.each(children, function(child) {
-                        var childId = child.id;
-                        if (!that.collection.get(childId)) {
-                            newChildren.push(childId);
-                        }
-                    })
-                    if (newChildren.length) {
-                        // reload if new child ID we don't recognise
-                        this.collection.includeEntities(newChildren);
-                        this.collection.fetch()
-                    }
-                }
+        nodeClicked: function(event) {
+            var $treebox = $(event.currentTarget).closest('.tree-box');
+            var id = $treebox.data('entityId');
+            selectTreebox(id, $treebox, this);
+            return false;
+        },
 
-                $(node).html($newNode)
-                this.addEventsToNode($(node))
-            }
-            return true;
+        selectEntity: function(id) {
+            var $treebox = findMasterTreebox(id);
+            selectTreebox(id, $treebox, this);
         },
 
         renderFull: function() {
@@ -230,15 +232,13 @@ define([
                 that.$el.append(_.template(TreeEmptyHtml));
             } else {
                 _.each(this.collection.getApplications(), function(appId) {
-                    that.updateNode(appId, null, true);
-                });
-                
-                _.each(this.collection.getNonApplications(), function(id) {
-                    that.updateNode(id);
+                    var entity = that.collection.get(appId);
+                    var $treebox = getOrCreateApplicationTreebox(entity.id, entity.name,
that);
+                    updateTreeboxContent(entity, $treebox, that);
                 });
             }
 
-            this.highlightEntity();
+            // this.highlightEntity();
 
             // Render the details for the selected entity.
             if (this.detailsView) {
@@ -248,8 +248,7 @@ define([
                 if (!this.collection.isEmpty()) {
                     var app0 = this.collection.first().id;
                     _.defer(function () {
-                        if (!that.selectedEntityId)
-                            that.displayEntityId(app0, app0);
+                        that.selectEntity(app0);
                     });
                 } else {
                     _.defer(function() {
@@ -261,30 +260,6 @@ define([
             return this;
         },
 
-        addEventsToNode: function($node) {
-            var that = this;
-
-            // show the "light-popup" (expand / expand all / etc) menu
-            // if user hovers for 500ms. surprising there is no option for this (hover delay).
-            // also, annoyingly, clicks around the time the animation starts don't seem to
get handled
-            // if the click is in an overlapping reason; this is why we position relative
top: 12px in css
-            $('.light-popup', $node).parent().parent().hover(
-                    function(parent) {
-                        that.cancelHoverTimer();
-                        that.hoverTimer = setTimeout(function() {
-                            var menu = $(parent.currentTarget).find('.light-popup')
-                            menu.show()
-                        }, 500);
-                    },
-                    function(parent) {
-                        that.cancelHoverTimer();
-                        var menu = $(parent.currentTarget).find('.light-popup')
-                        menu.hide()
-                        // hide all others too
-                        $('.light-popup').hide()
-                    });
-        },
-
         cancelHoverTimer: function() {
             if (this.hoverTimer != null) {
                 clearTimeout(this.hoverTimer);
@@ -292,42 +267,17 @@ define([
             }
         },
 
-        displayEntity: function(event) {
-            if (event.metaKey || event.shiftKey)
-                // trying to open in a new tab, do not act on it here!
-                return;
-            event.preventDefault();
-            var $nodeSpan = $(event.currentTarget);
-            var $nodeA = $nodeSpan.children('a').first();
-            var entityId = $nodeSpan.closest('.tree-box').data("entityId");
-            var href = $nodeA.attr('href');
-            var tab = (this.detailsView)
-                    ? this.detailsView.$el.find(".tab-pane.active").attr("id")
-                    : undefined;
-            if (href) {
-                if (tab) {
-                    href = href+"/"+tab;
-                    stateId = entityId+"/"+tab;
-                    this.preselectTab(tab);
-                }
-                Backbone.history.navigate(href);
-                this.displayEntityId(entityId, $nodeSpan.data("app-id"));
-            } else {
-                log("no a.href in clicked target");
-                log($nodeSpan);
-            }
-        },
-
         displayEntityId: function (id, appName, afterLoad) {
             var that = this;
-            this.highlightEntity(id);
+            //this.highlightEntity(id);
 
             var entityLoadFailed = function() {
                 return that.displayEntityNotFound(id);
             };
 
             if (appName === undefined) {
-                appName = $("#span-"+id).data("app-id")
+                var $treebox = findMasterTreebox(id);
+                appName = $treebox.children(".entity_tree_node").data("app-id");
             }
             if (appName === undefined) {
                 if (!afterLoad) {
@@ -391,56 +341,76 @@ define([
             this.cancelHoverTimer();
             $('.light-popup').hide();
             // don't let other events interfere
-            return false
-        },
-
-        hideChildrenOf: function($treeBox, recurse) {
-            var that = this;
-            if (recurse) {
-                $treeBox.children('.node-children').children().each(function (index, childBox)
{
-                    that.hideChildrenOf($(childBox), recurse)
-                });
-            }
-            $treeBox.children('.node-children').slideUp(300);
-            $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-down').addClass('icon-chevron-right');
+            return false;
         },
 
         showChildrenOf: function($treeBox, recurse) {
-            var that = this;
-            var idToExpand = $treeBox.children('.entity_tree_node_wrapper').attr('id');
+            var $wrapper = $treeBox.children('.entity_tree_node_wrapper');
+            var $childContainer = $treeBox.children('.node-children');
+            var idToExpand = $treeBox.data('entityId');
             var model = this.collection.get(idToExpand);
             if (model == null) {
                 // not yet loaded; parallel thread should load
                 return;
             }
-            var children = model.get('children');
+
+            var that = this;
+            var children = model.get('children'); // entity summaries: {id: ..., name: ...}
+            var renderChildrenAsIndirect = $treeBox.hasClass("indirect");
             _.each(children, function(child) {
-                var id = child.id;
-                if (!$('#'+id, that.$el).length)
-                    // load, but only if necessary
-                    that.updateNode(id, idToExpand) 
-            })
-            if (this.collection.includeEntities(children)) {
+                var $treebox = getOrCreateChildTreebox(child.id, child.name, renderChildrenAsIndirect,
$treeBox);
+                var model = that.collection.get(child.id);
+                if (model) {
+                    updateTreeboxContent(model, $treebox, that);
+                }
+            });
+            var members = model.get('members'); // entity summaries: {id: ..., name: ...}
+            _.each(members, function(member) {
+                var $treebox = getOrCreateChildTreebox(member.id, member.name, true, $treeBox);
+                var model = that.collection.get(member.id);
+                if (model) {
+                    updateTreeboxContent(model, $treebox, that);
+                }
+            });
+
+            if (this.collection.includeEntities(_.union(children, members))) {
                 // we have to load entities before we can proceed
                 this.collection.fetch({
                     success: function() {
                         if (recurse) {
-                            $treeBox.children('.node-children').children().each(function
(index, childBox) {
-                                _.defer( function() { that.showChildrenOf($(childBox), recurse)
} );
+                            $childContainer.children('.tree-box').each(function () {
+                                var $treebox = $(this);
+                                _.defer(function() {
+                                    that.showChildrenOf($treebox, recurse);
+                                });
                             });
                         }
                     }
-                })
+                });
             }
-            $treeBox.children('.node-children').slideDown(300);
-            $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down');
+
+            $childContainer.slideDown(300);
+            $wrapper.find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down');
             if (recurse) {
-                $treeBox.children('.node-children').children().each(function (index, childBox)
{
-                    that.showChildrenOf($(childBox), recurse);
+                $childContainer.children('.tree-box').each(function () {
+                    that.showChildrenOf($(this), recurse);
                 })
             }
         },
 
+        hideChildrenOf: function($treeBox, recurse) {
+            var $wrapper = $treeBox.children('.entity_tree_node_wrapper');
+            var $childContainer = $treeBox.children('.node-children');
+            if (recurse) {
+                var that = this;
+                $childContainer.children('.tree-box').each(function () {
+                    that.hideChildrenOf($(this), recurse);
+                });
+            }
+            $childContainer.slideUp(300);
+            $wrapper.find('.tree-node-state').removeClass('icon-chevron-down').addClass('icon-chevron-right');
+        },
+
         /**
          * Causes the tab with the given name to be selected automatically when
          * the view is next rendered.
@@ -466,11 +436,11 @@ define([
                 this.detailsView.close();
             }
             this.detailsView = new EntityDetailsView({
-                model:entitySummary,
-                application:app,
-                appRouter:this.options.appRouter,
-                preselectTab:whichTab,
-                preselectTabDetails:this.currentTabDetails,
+                model: entitySummary,
+                application: app,
+                appRouter: this.options.appRouter,
+                preselectTab: whichTab,
+                preselectTabDetails: this.currentTabDetails,
             });
 
             this.detailsView.on("entity.expunged", function() {
@@ -491,28 +461,6 @@ define([
             this.detailsView.render( $("div#details") );
         },
 
-        highlightEntity: function(id) {
-            if (id) this.selectedEntityId = id;
-            else id = this.selectedEntityId;
-
-            $(".entity_tree_node_wrapper").removeClass("active");
-            if (id) {
-                var $selectedNode = $(".entity_tree_node_wrapper#"+id);
-                // make this node active
-                $selectedNode.addClass("active");
-
-                // open the parent nodes if needed
-                var $nodeToOpenInParent = $selectedNode;
-                while ($nodeToOpenInParent.length && !$nodeToOpenInParent.is(':visible'))
{
-                    $nodeToOpenInParent = $nodeToOpenInParent.closest('.node-children').closest('.tree-box');
-                    this.showChildrenOf($nodeToOpenInParent);
-                }
-
-                // if we want to auto-expand the children of the selected node:
-//              this.showChildrenOf($selectedNode.closest('.tree-box'), false)
-            }
-        }
     });
 
-    return ApplicationTreeView;
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/345daaa0/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html b/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html
index 88e671a..d1a3d74 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html
@@ -22,11 +22,14 @@ under the License.
     var isLoaded = (model ? true : false);
     var isApp = (parentId ? false : true);
 
+    // Only emphasise applications at the top level, not as indirect references.
+    isApp &= !indirect;
+
     if (!isLoaded) {
 %>
         <i>Loading... (<%= id %>)</i>
 <%  } else {
-        var hasChildren = model.hasChildren();
+        var hasChildren = model.hasChildren() || model.hasMembers();
         var iconUrl = model.get('iconUrl');
 
         var entityIconSize = isApp ? 40 : 30;
@@ -38,7 +41,7 @@ under the License.
 %>
 
   <span class="entity_tree_node name entity" id="span-<%= id %>" 
-        data-entity-type="<%= model.get('type') %>" data-parent-id="<%= parentId
%>" data-app-id="<%= model.get('applicationId') %>">
+        data-entity-id="<%= id %>" data-entity-type="<%= model.get('type') %>"
data-parent-id="<%= parentId %>" data-app-id="<%= model.get('applicationId') %>">
     <a href="#v1/applications/<%= model.get('applicationId') %>/entities/<%=
id %>">
 
       <div style="min-width: <%= statusColumnWidth + (iconUrl ? entityIconSize : 6)%>px;
min-height: <%= minHeight %>px; max-height: 40px; display: inline-block; margin-right:
4px; vertical-align: middle;">
@@ -68,7 +71,10 @@ under the License.
             <img src="<%= iconUrl %>" style="max-width: <%= entityIconSize %>px;
max-height: <%= entityIconSize %>px; position: absolute; left: <%= statusColumnWidth
%>px; top: 0; bottom: 0; margin: auto;">
         <% } %>
       </div>
-     
+
+      <% if (indirect) { %>
+        <i class="indirection-icon icon-share-alt"></i>
+      <% } %>
       <span style="max-height: 18px; padding-right: 6px; position: relative; margin: auto;
top: 2px; bottom: 0;"><%= model.get('name') %></span>
 
     </a>


Mime
View raw message