REST api supports batch-style fetch, and app tree is lazily (efficiently) loaded -- so much better for big trees -- and updates dynamically, including status icon
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/869239d1 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/869239d1 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/869239d1 Branch: refs/heads/0.6.0 Commit: 869239d175619aa7a4152405be5dc31f13911187 Parents: e03bb7b Author: Alex Heneveld <[email protected]> Authored: Tue Sep 17 12:22:08 2013 +0100 Committer: Alex Heneveld <[email protected]> Committed: Wed Sep 18 09:30:06 2013 +0100 ---------------------------------------------------------------------- usage/jsgui/src/main/webapp/assets/css/base.css | 6 +- .../src/main/webapp/assets/js/model/app-tree.js | 38 ++- .../webapp/assets/js/view/activity-details.js | 6 +- .../assets/js/view/application-explorer.js | 15 +- .../webapp/assets/js/view/application-tree.js | 282 +++++++++++++------ .../webapp/assets/js/view/entity-details.js | 4 +- .../src/main/webapp/assets/js/view/viewutils.js | 8 +- .../main/webapp/assets/tpl/apps/tree-item.html | 52 ++-- 8 files changed, 278 insertions(+), 133 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/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 bd69c3e..5904082 100644 --- a/usage/jsgui/src/main/webapp/assets/css/base.css +++ b/usage/jsgui/src/main/webapp/assets/css/base.css @@ -539,7 +539,7 @@ ol.tree { color: #54932b !important; text-decoration: none; } -.entity_tree_node.active { +.entity_tree_node_wrapper.active .entity_tree_node { font-weight: bold; } #tree label { @@ -568,7 +568,9 @@ line-height: 18px; position: relative; float: left; } - +.entity_tree_node_wrapper { + position: relative; +} .tree-box { border: 1px solid #AAA; border-right: 0px; http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js b/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js index 91bc63d..0b2dbde 100644 --- a/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js +++ b/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js @@ -1,6 +1,6 @@ define([ - "backbone" -], function (Backbone) { + "underscore", "backbone" +], function (_, Backbone) { var AppTree = {} @@ -9,21 +9,47 @@ define([ return { id:"", name:"", + type:"", iconUrl:"", - children:[] + serviceUp:"", + serviceState:"", + applicationId:"", + parentId:"", + childrenIds:[] } }, getDisplayName:function () { - return this.get("name") //+ ":" + this.get("id") + return this.get("name") }, hasChildren:function () { - return this.get("children").length > 0 + return this.get("childrenIds").length > 0 } }) AppTree.Collection = Backbone.Collection.extend({ model:AppTree.Model, - url:"/v1/applications/tree" + includedEntities: [], + getApplications: function () { + var entities = []; + _.each(this.models, function(it) { if (it.get('id')==it.get('applicationId')) entities.push(it.get('id')) }); + return entities; + }, + getNonApplications: function () { + var entities = []; + _.each(this.models, function(it) { if (it.get('id')!=it.get('applicationId')) entities.push(it.get('id')) }); + return entities; + }, + includeEntities: function (entities) { + var oldLength = this.includedEntities.length; + this.includedEntities = _.uniq(this.includedEntities.concat(entities)) + return (this.includedEntities.length > oldLength); + }, + url: function() { + if (this.includedEntities.length) + return "/v1/applications/fetch?items="+this.includedEntities.join(","); + else + return "/v1/applications/fetch"; + } }) return AppTree http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js b/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js index c8079fd..1466bca 100644 --- a/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js +++ b/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js @@ -80,7 +80,7 @@ define([ } this.renderSubtasks() - this.callPeriodically("refreshNow", function () { + this.callPeriodically("refresh-activities-now", function () { this.refreshNow() }, 1000); @@ -158,7 +158,7 @@ define([ this.children = new TaskSummary.Collection() this.children.url = this.task.get("links").children this.children.on("reset", this.renderChildren, this) - this.callPeriodically("refreshChildren", function () { + this.callPeriodically("refresh-activity-children", function () { that.children.fetch({reset: true}); }, 3000); that.children.fetch({reset: true}); @@ -169,7 +169,7 @@ define([ that.collection = new TaskSummary.Collection() that.collection.url = entity.links.activities that.collection.on("reset", this.renderSubtasks, this) - that.callPeriodically("refreshSubmittedTasks", function () { + that.callPeriodically("refresh-activity-bgtasks", function () { that.collection.fetch({reset: true}); }, 3000); that.collection.fetch({reset: true}); http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/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 d6837e7..4cd2008 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 @@ -15,11 +15,12 @@ define([ id:'application-explorer', template:_.template(PageHtml), events:{ - 'click .application-tree-refresh': 'refreshApplications', + 'click .application-tree-refresh': 'refreshApplicationsInPlace', 'click #add-new-application':'createApplication', 'click .delete':'deleteApplication' }, initialize:function () { + var that = this; this.$el.html(this.template({})) $(".nav1").removeClass("active"); $(".nav1_apps").addClass("active"); @@ -28,8 +29,10 @@ define([ this.treeView = new ApplicationTreeView({ collection:this.collection }) - this.$('div#app-tree').html(this.treeView.render().el) - this.treeView.render() + this.$('div#app-tree').html(this.treeView.renderFull().el) + this.refreshApplications(); + that.callPeriodically("entity-tree-apps", + function() { that.refreshApplicationsInPlace() }, 3000) }, beforeClose:function () { this.collection.off("reset", this.render) @@ -43,6 +46,10 @@ define([ this.collection.fetch({reset: true}) return false }, + refreshApplicationsInPlace:function () { + this.collection.fetch() + return false + }, show: function(entityId) { this.treeView.displayEntityId(entityId) }, @@ -57,7 +64,7 @@ define([ } var wizard = new AppAddWizard({ appRouter:that.options.appRouter, - callback:function() { that.refreshApplications() } + callback:function() { that.refreshApplicationsInPlace() } }) this._modal = wizard this.$(".add-app #modal-container").html(wizard.render().el) http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/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 9e1cc73..db1c679 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 @@ -24,16 +24,130 @@ define([ }, initialize:function () { - this.collection.on('reset', this.render, this) + 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) _.bindAll(this); }, beforeClose:function () { - this.collection.off("reset", this.render) + 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 (eventName == "change" || eventName == "remove" || eventName == "add" || + eventName == "reset" || + // above are handled; below is no-op + eventName == "sync" || eventName == "request") + return; + + // don't think we get other events, but just in case: + log("unhandled model event") + log(eventName) + log(event) + log(x) + }, + + removeNode: function(id) { + $('#'+id, this.$el).parent().remove() + }, + + updateNode: function(id, parentId, isApp) { + var that = this; + var nModel = that.collection.get(id); + var nEl = $('#'+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 && nEl && nEl.parent().data('depth')==0) isApp = true; + } - render:function () { + if (!isApp && !parentId && nModel) parentId = nModel.get('parentId'); + if (!isApp && !parentId && nEl) parentId = nEl.closest("entity_tree_node_wrapper").data('parentId'); + if (!isApp && !parentId) { + log("no parentId yet available for "+id+"; skipping;") + return false; + } + + var statusIconUrl = nModel ? ViewUtils.computeStatusIcon(nModel.get("serviceUp"),nModel.get("serviceState")) : null; + + var nEl2 = this.template({ + id:id, + parentId:parentId, + model:nModel, + statusIconUrl:statusIconUrl + }) + + if (!nEl.length) { + // node does not exist, so add it + var pElC, depth; + + if (isApp) { + pElC = $('.lozenge-app-tree-wrapper', that.$el); + if (!pElC.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>'); + pElC = $('.lozenge-app-tree-wrapper', that.$el); + } + depth = 0; + } else { + var pEl = $('#'+parentId, that.$el) + if (!pEl.length) { + // see if we can load the parent + if (this.updateNode(parentId)) { + pEl = $('#'+parentId, that.$el); + if (!pEl.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; + } + } + pElC = pEl.parent().children('.node-children') + depth = pEl.parent().data("depth")+1 + } + + // add it, with surrounding html, in parent's node-children child + var nEl3 = $( + '<div 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, nEl3).html(nEl2); + $(pElC).append(nEl3); + this.addEventsToNode($(pElC)) + } else { + // updating + $(nEl).html(nEl2) + this.addEventsToNode($(nEl)) + } + return true + }, + + renderFull:function () { var that = this this.$el.empty() @@ -41,16 +155,13 @@ define([ if (this.collection.isEmpty()) { that.$el.append(_.template(TreeEmptyHtml)) } else { - that.$el.append( - '<div class="navbar_main_wrapper treeloz">'+ - '<div id="tree-list" class="navbar_main treeloz">'+ - '<div class="lozenge-app-tree-wrapper">'); - var node = $('div.lozenge-app-tree-wrapper', that.$el); + _.each(this.collection.getApplications(), + function(appId) { that.updateNode(appId, null, true) }) - this.collection.each(function (app) { - node.append(that.buildTree(app)) - }) + _.each(this.collection.getNonApplications(), + function(id) { that.updateNode(id) }) } + this.highlightEntity(); // Render the details for the selected entity. @@ -74,61 +185,15 @@ define([ return this }, - buildTree:function (application) { - var that = this, - $template = $(this.template({ - id:application.get("id"), - type:"application", - hasChildren: application.hasChildren(), - parentApp:application.get("id"), - displayName:application.get("name"), - iconUrl:application.get("iconUrl"), - statusIconUrl: ViewUtils.computeStatusIcon(application.get("serviceUp"),application.get("serviceState")), - depth: 0 - })), - treeFromEntity = function (entity, depth) { - var $entityTpl - - if (entity.hasChildren()) { - $entityTpl = $(that.template({ - id:entity.get("id"), - type:"entity", - hasChildren: true, - parentApp:application.get("id"), - displayName:entity.getDisplayName(), - iconUrl:entity.get("iconUrl"), - statusIconUrl: ViewUtils.computeStatusIcon(entity.get("serviceUp"),entity.get("serviceState")), - depth: depth - })) - var $parentTpl = $entityTpl.find("#children") - _.each(entity.get("children"), function (childEntity) { - $parentTpl.append(treeFromEntity(new AppTree.Model(childEntity), depth+1)) - }) - } else { - $entityTpl = $(that.template({ - id:entity.get("id"), - type:"leaf", - hasChildren: false, - parentApp:application.get("id"), - displayName:entity.getDisplayName(), - iconUrl:entity.get("iconUrl"), - statusIconUrl: ViewUtils.computeStatusIcon(entity.get("serviceUp"),entity.get("serviceState")), - depth: depth - })) - } - return $entityTpl - } - - // start rendering from initial children of the application - var $tree = $template.find("#children") - _.each(application.get("children"), function (entity) { - $tree.append(treeFromEntity(new AppTree.Model(entity), 1)) - }) - $('a', $tree).click(function(e) { e.preventDefault(); }) + addEventsToNode: function($node) { + var that = this; + + // prevent default click-handling (not sure needed?) + $('a', $node).click(function(e) { e.preventDefault(); }) // show the "light-popup" (expand / expand all / etc) menu // if user hovers for 500ms. surprising there is no option for this. - $('.light-popup', $template).parent().parent().hover( + $('.light-popup', $node).parent().parent().hover( function (parent) { that.cancelHoverTimer(); that.hoverTimer = setTimeout(function() { @@ -142,8 +207,6 @@ define([ menu.hide() $('.light-popup').hide() }) - - return $template }, cancelHoverTimer: function() { var that = this; @@ -157,7 +220,7 @@ define([ event.preventDefault(); var nodeSpan = $(event.currentTarget) var nodeA = $(event.currentTarget).children('a').first() - var entityId = nodeSpan.attr("id"), + var entityId = nodeSpan.parent().attr("id"), stateId = entityId, href = nodeA.attr('href'), tab = (this.detailsView) @@ -170,16 +233,14 @@ define([ this.preselectTab(tab) } window.history.pushState(stateId, "", href) - this.displayEntityId(entityId, $(event.currentTarget).data("parent-app")); + this.displayEntityId(entityId, nodeSpan.data("app-id")); } else { log("no a.href in clicked target") log(nodeSpan) } }, - - displayEntityId:function (id, appName) { + displayEntityId:function (id, appName, afterLoad) { var that = this; - console.debug("Displaying entity: " + id); this.highlightEntity(id) var entityLoadFailed = function() { @@ -187,14 +248,25 @@ define([ }; if (appName === undefined) { - appName = $("span.entity_tree_node#"+id).data("parent-app") + appName = $("#span-"+id).data("app-id") } if (appName === undefined) { - // no such app - console.error("Couldn't find a parent application for entity: " + id); - return entityLoadFailed(); + if (!afterLoad) { + // try a reload if given an ID we don't recognise + this.collection.includeEntities([id]); + this.collection.fetch({ + success: function() { _.defer(function() { that.displayEntityId(id, appName, true); }); }, + error: function() { _.defer(function() { that.displayEntityId(id, appName, true); }); } + }); + ViewUtils.fadeToIndicateInitialLoad($("div#details")) + return; + } else { + // no such app + entityLoadFailed(); + return; + } } - + var app = new Application.Model(), entitySummary = new EntitySummary.Model; @@ -203,11 +275,10 @@ define([ // in case the server response time is low, fade out while it refreshes // (since we can't show updated details until we've retrieved app + entity details) - $("div#details").fadeTo(1000, 0.3) + ViewUtils.fadeToIndicateInitialLoad($("div#details")) $.when(app.fetch(), entitySummary.fetch()) .done(function() { - $("div#details").stop().fadeTo(200, 1) that.showDetails(app, entitySummary); }) .fail(entityLoadFailed); @@ -215,6 +286,7 @@ define([ displayEntityNotFound: function(id) { $("div#details").html(notFoundTemplate({"id": id})); + ViewUtils.cancelFadeOnceLoaded($("div#details")) }, treeChange: function(event) { @@ -230,7 +302,7 @@ define([ this.hideChildrenOf($treeBox, true) } else { // default - toggle - if ($treeBox.children('#children').is(':visible')) { + if ($treeBox.children('.node-children').is(':visible')) { this.hideChildrenOf($treeBox, false) } else { this.showChildrenOf($treeBox, false) @@ -244,20 +316,40 @@ define([ }, hideChildrenOf: function($treeBox, recurse) { var that = this; - $treeBox.children('#children').slideUp(300) - $treeBox.children('.tree-node').find('.tree-node-state').removeClass('icon-chevron-down').addClass('icon-chevron-right') if (recurse) { - $treeBox.children('#children').children().each(function (index, childBox) { + $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') }, showChildrenOf: function($treeBox, recurse) { var that = this; - $treeBox.children('#children').slideDown(300) - $treeBox.children('.tree-node').find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down') + var idToExpand = $treeBox.children('.entity_tree_node_wrapper').attr('id'); + var model = this.collection.get(idToExpand); + if (model==null) { + // not yet loaded; parallel thread should load + return; + } + var childrenIds = model.get('childrenIds'); + _.each(childrenIds, function(id) { that.updateNode(id, idToExpand) }) + if (this.collection.includeEntities(childrenIds)) { + // 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) } ); + }) + } + } + }) + } + $treeBox.children('.node-children').slideDown(300) + $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down') if (recurse) { - $treeBox.children('#children').children().each(function (index, childBox) { + $treeBox.children('.node-children').children().each(function (index, childBox) { that.showChildrenOf($(childBox), recurse) }) } @@ -272,6 +364,8 @@ define([ }, showDetails: function(app, entitySummary) { + ViewUtils.cancelFadeOnceLoaded($("div#details")) + var whichTab = this.currentTab if (whichTab === undefined) { whichTab = "summary"; @@ -295,12 +389,22 @@ define([ highlightEntity:function (id) { if (id) this.selectedEntityId = id else id = this.selectedEntityId - $("span.entity_tree_node").removeClass("active") + + $(".entity_tree_node_wrapper").removeClass("active") if (id) { - var $selectedNode = $("span.entity_tree_node#"+id); - $selectedNode.addClass("active") - // if we wanted to auto-expand the children of the selected node: -// this.showChildrenOf($selectedNode.parents('#app-tree .tree-box'), false) + 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) } } }) http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js index 11aa62e..0028b40 100644 --- a/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js +++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js @@ -61,8 +61,8 @@ define([ }, tabSelected: function(event) { var tabName = $(event.currentTarget).attr("href").slice(1) - var entityId = $("#app-tree span.active").attr("id") - var entityHref = $("#app-tree span.active a").attr("href") + var entityId = $("#app-tree .entity_tree_node_wrapper.active").attr("id") + var entityHref = $("#app-tree .entity_tree_node_wrapper.active a").attr("href") if (entityId && entityHref) { window.history.pushState(entityId+"/"+tabName, "", entityHref+"/"+tabName); http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js b/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js index ae9dbb7..a30635c 100644 --- a/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js +++ b/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js @@ -226,18 +226,18 @@ define([ processTooltips: function($el) { $el.find('*[rel="tooltip"]').tooltip(); }, - fadeToIndicateInitialLoad: function($table) { + fadeToIndicateInitialLoad: function($el) { // in case the server response time is low, fade out while it refreshes // (since we can't show updated details until we've retrieved app + entity details) try { - $table.fadeTo(1000, 0.3); + $el.fadeTo(1000, 0.3); } catch (e) { // ignore - normal during tests } }, - cancelFadeOnceLoaded: function($table) { + cancelFadeOnceLoaded: function($el) { try { - $table.stop().fadeTo(200, 1); + $el.stop(true, false).fadeTo(200, 1); } catch (e) { // ignore - normal during tests } http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/869239d1/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 82fcef3..4eb7e50 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 @@ -1,26 +1,34 @@ -<div class="toggler-group tree-box <%= - depth==0 ? "outer" : "inner "+(depth%2==1 ? "depth-odd" : "depth-even")+(depth==1 ? " depth-first" : "") %>"> - <div class="tree-node"> - <span class="entity_tree_node name entity" id="<%= id %>" data-entity-type="<%= type %>" data-parent-app="<%= parentApp %>"> - <a href="#/v1/applications/<%= parentApp %>/entities/<%= id %>"> +<% + var isLoaded = (model ? true : false); + var isApp = (parentId ? false : true); - <% - var isApp = type == "application"; + if (!isLoaded) { +%> + <i>Loading... (<%= id %>)</i> +<% } else { + var hasChildren = model.hasChildren(); + var iconUrl = model.get('iconUrl'); + var entityIconSize = isApp ? 40 : 30; var statusIconSize = isApp ? 24 : 16; - var chevronLeft = isApp ? 5.5 : 1.5; + var chevronLeft = (isApp ? 5.5 : 1.5); var minHeight = hasChildren && statusIconUrl ? entityIconSize : 24; var statusColumnWidth = hasChildren || statusIconUrl || (!isApp && !iconUrl /* for children, insert space so things line up */) ? statusIconSize : 0; - %> - <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;"> +%> + + <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') %>"> + <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;"> <% if (statusIconUrl) { %> <div style="position: absolute; left: 0px; margin: auto; top: <%= isApp && hasChildren ? 3 : 2 %>px;<% if (!hasChildren) { %> bottom: 0px;<% } %>"> - <img src="<%= statusIconUrl %>" style="max-width: <%= statusIconSize %>px; max-height: <%= statusIconSize %>px; margin: auto; position: absolute; top: 0px;<% if (!hasChildren) { %> bottom: 0px;<% } %>"> + <img src="<%= statusIconUrl %>" style="max-width: <%= statusIconSize %>px; max-height: <%= statusIconSize %>px; margin: auto; position: absolute; top: -1px;<% if (!hasChildren) { %> bottom: 0px;<% } %>"> </div> <% } %> <% if (hasChildren) { %> - <div style="position: absolute; left: <%= chevronLeft %>px; margin: auto; <%= statusIconUrl ? "bottom: 1px;" : isApp ? "top: 6px;" : "top: 6px;" %>"> + <div style="position: absolute; left: <%= chevronLeft %>px; margin: auto; <%= statusIconUrl ? "bottom: -1px;" : isApp ? "top: 6px;" : "top: 6px;" %>"> <div class="toggler-icon icon-chevron-right tree-node-state tree-change"> <div class="light-popup"> <div class="light-popup-body"> @@ -34,16 +42,14 @@ </div> </div> <% } %> - <% if (iconUrl) { %> - <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 (iconUrl) { %> + <img src="<%= iconUrl %>" style="max-width: <%= entityIconSize %>px; max-height: <%= entityIconSize %>px; position: absolute; left: <%= statusColumnWidth %>px; top: 0; bottom: 0; margin: auto;"> + <% } %> + </div> - <span style="max-height: 18px; position: relative; margin: auto; top: 2px; bottom: 0;"><%= displayName %></span> + <span style="max-height: 18px; padding-right: 6px; position: relative; margin: auto; top: 2px; bottom: 0;"><%= model.get('name') %></span> - </a> - </span> - </div> - <div id="children" class="toggler-target hide"> - </div> -</div> + </a> + </span> + +<% } %> \ No newline at end of file
