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 <[email protected]> Authored: Thu Oct 1 08:49:51 2015 +0100 Committer: Alasdair Hodge <[email protected]> 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>
