You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/03/03 13:29:52 UTC

[07/10] brooklyn-ui git commit: fix browser forward-back navigation, activity tab stale, and sort order

fix browser forward-back navigation, activity tab stale, and sort order


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

Branch: refs/heads/master
Commit: e22ba57ed54e21f0b5bd492ab804da9731840e05
Parents: 3081c67
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Mar 3 01:48:58 2016 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Mar 3 01:49:54 2016 +0000

----------------------------------------------------------------------
 src/main/webapp/assets/js/router.js             |   6 +-
 .../assets/js/view/application-explorer.js      |  43 ++++---
 .../webapp/assets/js/view/application-tree.js   | 115 +++++++++++++------
 .../webapp/assets/js/view/entity-details.js     |   8 +-
 4 files changed, 117 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e22ba57e/src/main/webapp/assets/js/router.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/router.js b/src/main/webapp/assets/js/router.js
index 1d6dae4..aaccf70 100644
--- a/src/main/webapp/assets/js/router.js
+++ b/src/main/webapp/assets/js/router.js
@@ -197,10 +197,12 @@ define([
             this.appTree.fetch({success:function () {
                 var appExplorer = new ExplorerView({
                     collection:that.appTree,
-                    appRouter:that
-                })
+                    appRouter:that,
+                    initialTrail:trail
+                });
                 that.showView("#application-content", appExplorer)
                 if (trail !== undefined) appExplorer.show(trail)
+                else appExplorer.showDefaultSelection();
             }})
         },
         catalogPage: function (catalogItemKind, id) {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e22ba57e/src/main/webapp/assets/js/view/application-explorer.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/view/application-explorer.js b/src/main/webapp/assets/js/view/application-explorer.js
index 475432a..cc5eb0b 100644
--- a/src/main/webapp/assets/js/view/application-explorer.js
+++ b/src/main/webapp/assets/js/view/application-explorer.js
@@ -49,10 +49,12 @@ define([
                 collection:this.collection,
                 appRouter:this.options.appRouter
             })
+            if (this.options.initialTrail) this.show(this.options.initialTrail, true);
             this.treeView.on('entitySelected', function(e) {
                this.displayEntityId(e.id, e.get('applicationId'), false);
             }, this);
-            this.$('div#app-tree').html(this.treeView.renderFull().el)
+            // don't draw these -- wait for the "show" command
+            this.$('div#app-tree').html(this.treeView.renderFull().el);
             this.$('div#details').html(EntityDetailsEmptyHtml);
 
             ViewUtils.fetchRepeatedlyWithDelay(this, this.collection)
@@ -68,7 +70,7 @@ define([
             if (this.detailsView)
                 this.detailsView.close();
         },
-        show: function(entityId) {
+        show: function(entityId, isPre) {
             var tab = "";
             var tabDetails = "";
             if (entityId) {
@@ -88,8 +90,15 @@ define([
                 this.preselectTab(tab, tabDetails);
             }
             if (!this.treeView.selectEntity(entityId)) {
-                // needs a load
-                this.displayEntityId(entityId);
+                // needs a load, but only allowed if not pre-loading
+                if (isPre) { /* don't allow loading if this is a "pre-show" */ }
+                else { this.displayEntityId(entityId); }
+            }
+        },
+        showDefaultSelection: function() {
+            if (!this.treeView.selectedEntityId && !this.treeView.requestedEntityId) {
+                var firstApp = _.first(this.collection.getApplications());
+                if (firstApp) this.treeView.selectEntity(firstApp);
             }
         },
         createApplication:function () {
@@ -124,22 +133,27 @@ define([
             var that = this;
             ViewUtils.cancelFadeOnceLoaded($("div#details"));
 
-            var whichTab = this.currentTab;
-            if (!whichTab) {
-                whichTab = "summary";
-                if (this.detailsView) {
-                    whichTab = this.detailsView.$el.find(".tab-pane.active").attr("id");
-                    this.detailsView.close();
-                }
-            }
             if (this.detailsView) {
+                // if view open, take that tab, but clear the details if the id is different
+                if (this.detailsView.model && this.detailsView.model.id && this.detailsView.model.id != entitySummary.id)
+                    this.detailsView.options.preselectTabDetails = null;
+                this.preselectTab(
+                    //this.detailsView.$el.find(".tab-pane.active").attr("id")
+                    this.detailsView.options.preselectTab,
+                    this.detailsView.options.preselectTabDetails
+                    );
                 this.detailsView.close();
             }
+            if (!this.currentTab) {
+                // else if nothing preselect, use summary
+                this.preselectTab("summary");
+            }
+            
             this.detailsView = new EntityDetailsView({
                 model: entitySummary,
                 application: app,
                 appRouter: this.options.appRouter,
-                preselectTab: whichTab,
+                preselectTab: this.currentTab,
                 preselectTabDetails: this.currentTabDetails,
             });
 
@@ -159,6 +173,9 @@ define([
                 that.collection.fetch();
             });
             this.detailsView.render( $("div#details") );
+            // force the routes to be updated, and clear activities detail possibly (the tab is already shown)
+            // (this causes activities view to lose selected sub-tasks, but that's okay)
+            this.detailsView.openTab(this.currentTab + (this.currentTabDetails ? '/'+this.currentTabDetails : ''));
         },
         displayEntityId: function (id, appName, afterLoad) {
             var that = this;

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e22ba57e/src/main/webapp/assets/js/view/application-tree.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/view/application-tree.js b/src/main/webapp/assets/js/view/application-tree.js
index 5a6d114..0d9f4fb 100644
--- a/src/main/webapp/assets/js/view/application-tree.js
+++ b/src/main/webapp/assets/js/view/application-tree.js
@@ -33,8 +33,8 @@ define([
         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 findRootTreebox = function(id, $scope) {
+        return $('.lozenge-app-tree-wrapper', $scope).children('.tree-box[data-entity-id="' + id + '"]', this.$el);
     };
 
     var findChildTreebox = function(id, $parentTreebox) {
@@ -45,24 +45,17 @@ define([
         return $('.tree-box[data-entity-id="' + id + '"]:not(.indirect)', $scope);
     };
 
-    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 sortKeyOfIdName = function(id, name) {
+        return (name ? name.toLowerCase() : "~~~") + "     " + id.toLowerCase();
+    };
+    var sortKeyOfEntity = function(entity) {
+        return sortKeyOfIdName(entity.id, entity.get('name'));
+    };
+    
+    var insertSorted = function($treebox, $domParent) {
         var placed = false;
         var contender = $(".toggler-group", $domParent).first();
+        var sortKey = $treebox.data('sort-key');
         while (contender.length && !placed) {
             var contenderKey = contender.data("sort-key");
             if (sortKey < contenderKey) {
@@ -77,11 +70,29 @@ define([
         }
         return $treebox;
     };
+    var createEntityTreebox = function(id, name, $domParent, depth, indirect) {
+        // Tildes in sort key force entities with no name to bottom of list (z < ~).
+        var sortKey = sortKeyOfIdName(id, name);
+        // 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>');
 
-    var getOrCreateApplicationTreebox = function(id, name, treeView) {
-        var $treebox = findRootTreebox(id);
+        // Insert into the passed DOM parent, maintaining sort order relative to siblings: name then id.
+        return insertSorted($treebox, $domParent);
+    }
+    
+    var getOrCreateApplicationTreebox = function(id, name, treeView, $el) {
+        if (!($el)) $el = treeView.$el;
+        var $treebox = findRootTreebox(id, $el);
         if (!$treebox.length) {
-            var $insertionPoint = $('.lozenge-app-tree-wrapper', treeView.$el);
+            var $insertionPoint = $('.lozenge-app-tree-wrapper', $el);
             if (!$insertionPoint.length) {
                 // entire view must be created
                 treeView.$el.html(
@@ -89,7 +100,7 @@ define([
                         '<div id="tree-list" class="navbar_main treeloz">'+
                         '<div class="lozenge-app-tree-wrapper">'+
                         '</div></div></div>');
-                $insertionPoint = $('.lozenge-app-tree-wrapper', treeView.$el);
+                $insertionPoint = $('.lozenge-app-tree-wrapper', $el);
             }
             $treebox = createEntityTreebox(id, name, $insertionPoint, 0, false);
         }
@@ -105,6 +116,9 @@ define([
     };
 
     var updateTreeboxContent = function(entity, $treebox, treeView) {
+        oldSortKey = $treebox.data('sort-key');
+        newSortKey = sortKeyOfEntity(entity);
+        
         var $newContent = $(treeView.template({
             id: entity.get('id'),
             parentId:  entity.get('parentId'),
@@ -122,6 +136,14 @@ define([
 
         $wrapper.html($newContent);
         addEventsToNode($treebox, treeView);
+        
+        if (newSortKey !== oldSortKey) {
+            // move this treebox to the right place in its parent
+            $treebox.data('sort-key', newSortKey);
+            $parent = $treebox.parent();
+            $treebox.detach();
+            insertSorted($treebox, $parent);
+        }
     };
 
     var addEventsToNode = function($node, treeView) {
@@ -148,22 +170,31 @@ define([
         if (!($treebox.length)) return false;
         if ($treebox.is(':visible')) return true;
         var $parentTreebox = $treebox.parent().closest('.tree-box');
-        if (!($parentTreebox) || $parentTreebox==$treebox) return false;
+        if (!($parentTreebox.length)) return true;  //means item is being rendered offscreen
+        if ($parentTreebox==$treebox) return false;  //should never happen
         if (!ensureTreeboxVisible($parentTreebox, treeView)) return false;
         treeView.showChildrenOf($parentTreebox, false);
         return true;
     };
     
     var selectTreebox = function(id, $treebox, treeView) {
-        $('.entity_tree_node_wrapper').removeClass('active');
+        treeView.requestedEntityId = id;
+        $('.entity_tree_node_wrapper', treeView.$el).removeClass('active');
         $treebox.children('.entity_tree_node_wrapper').addClass('active');
 
         var entity = treeView.collection.get(id);
         if (entity) {
-            ensureTreeboxVisible($treebox, treeView);
-            treeView.selectedEntityId = id;
-            treeView.trigger('entitySelected', entity);
-            return true;
+            if (ensureTreeboxVisible($treebox, treeView)) {
+                treeView.selectedEntityId = id;
+                treeView.requestedEntityId = null;
+                treeView.trigger('entitySelected', entity);
+                return true;
+            } else {
+                // children DOM nodes not yet existing; probably the model is not populated,
+                // e.g. using browser forward/back, maybe also if invoked before selected;
+                // callers should populate then re-select
+                return false;
+            }
         } else {
             // probably needs a load
             return false;
@@ -196,7 +227,6 @@ define([
             // 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).
-
             var $treebox;
             var parentId = entity.get('parentId');
             if (!parentId) {
@@ -204,8 +234,7 @@ define([
                 $treebox = getOrCreateApplicationTreebox(entity.id, entity.get('name'), this);
 
                 // Select the new app if there's no current selection.
-                if (!this.selectedEntityId)
-                    selectTreebox(entity.id, $treebox, this);
+                if (!this.selectedEntityId && !this.requestedEntityId) selectTreebox(entity.id, $treebox, this);
             } else {
                 // else create in parent
                 var parent = this.collection.get(parentId);
@@ -263,6 +292,8 @@ define([
 
         renderFull: function() {
             var that = this;
+            
+            // build up the new element in a hidden node
             this.$el.empty();
 
             // Display tree and highlight the selected entity.
@@ -275,14 +306,22 @@ define([
                     var $treebox = getOrCreateApplicationTreebox(entity.id, entity.name, that);
                     updateTreeboxContent(entity, $treebox, that);
                 });
+            
+                // expand to show selected or requested id
+                var target = this.selectedEntityId;
+                if (!target) target = this.requestedEntityId;
+                if (target) {
+                    // ensure this treebox is added and reselected
+                    var item = this.collection.get(target);
+                    if (item) {
+                        this.initialRender = true;
+                        this.entityAdded(item);
+                        this.selectEntity(target);
+                        this.initialRender = false;
+                    }
+                }
             }
 
-            // Select the first app if there's no current selection.
-            if (!this.selectedEntityId) {
-                var firstApp = _.first(this.collection.getApplications());
-                if (firstApp)
-                    this.selectEntity(firstApp);
-            }
             return this;
         },
 
@@ -377,7 +416,7 @@ define([
                 });
             }
 
-            $childContainer.slideDown(300);
+            $childContainer.slideDown(this.initialRender ? 0 : 300);
             $wrapper.find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down');
             if (recurse) {
                 $childContainer.children('.tree-box').each(function () {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e22ba57e/src/main/webapp/assets/js/view/entity-details.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/view/entity-details.js b/src/main/webapp/assets/js/view/entity-details.js
index f54c572..54e6d1b 100644
--- a/src/main/webapp/assets/js/view/entity-details.js
+++ b/src/main/webapp/assets/js/view/entity-details.js
@@ -107,11 +107,12 @@ define([
                     $(a).attr('href',entityHref+"/"+$(a).attr("data-target").slice(1));
                 });
             } else {
-                log("could not find entity href for tab");
+                // entity not available yet -- should be soon
+//                log("could not find entity href for tab");
             }
             if (this.options.preselectTab) {
                 var tabLink = this.$('a[data-target="#'+this.options.preselectTab+'"]');
-                var showFn = function() { tabLink.tab('show'); };
+                var showFn = function() { tabLink.tab('show', { duration: 0 }); };
                 if (optionalParent) showFn();
                 else _.defer(showFn);
             }
@@ -126,6 +127,9 @@ define([
             event.preventDefault();
             
             var tabName = $(event.currentTarget).attr("data-target").slice(1);
+            this.options.preselectTab = tabName;
+            this.options.preselectTabDetails = null;
+            
             var route = this.getTab(tabName);
             if (route) {
                 if (route[0]=='#') route = route.substring(1);