4084: Rework refresh event handling:
[arvados.git] / apps / workbench / app / assets / javascripts / tab_panes.js
index 411a009f5c6badd4d4f566d6632618351c19504f..7e2527eda6ba72a28bcde03ad1e75d75b2f9b915 100644 (file)
 // Load tab panes on demand. See app/views/application/_content.html.erb
 
-// Fire when a tab is selected/clicked. Check whether the content in
-// the corresponding pane is loaded (or is being loaded). If not,
-// start an AJAX request to load the content.
-$(document).on('shown.bs.tab', '[data-toggle="tab"]', function(e) {
-    var content_url = $(e.target).attr('data-pane-content-url');
-    var $pane = $($(e.target).attr('href'));
-    if ($pane.hasClass('loaded'))
+// Fire when a tab is selected/clicked.
+$(document).on('shown.bs.tab', '[data-toggle="tab"]', function(event) {
+    var tgt = $($(event.relatedTarget).attr('href'));
+    $(".pane-anchor", tgt).each(function (i, e) {
+        var a = $($(e).attr('href'));
+        a.removeClass("active");
+    });
+
+    tgt = $($(event.target).attr('href'));
+    $(".pane-anchor", tgt).each(function (i, e) {
+        var a = $($(e).attr('href'));
+        a.addClass("active");
+    });
+
+    $(event.target).trigger('arv:pane:reload');
+});
+
+// Ask a refreshable pane to reload via ajax.
+// Target of this event is the anchoring element that manages the pane.
+// Panes can be in one of four states: not loaded (no state classes), pane-loading, pane-loading+pane-stale, pane-loaded
+$(document).on('arv:pane:reload', function(e) {
+    e.stopPropagation();
+
+    if ($(e.target).hasClass('pane-loading')) {
+        // Already loading, mark stale to schedule a reload after this one.
+        console.log(e.target.id + " stale");
+        $(e.target).addClass('pane-stale');
         return;
-    $.ajax(content_url, {dataType: 'html', type: 'GET', context: $pane}).
-        done(function(data, status, jqxhr) {
-            $('> div > div', this).html(data);
-            $(this).addClass('loaded');
-            $(this).trigger('arv:pane:loaded');
-        }).fail(function(jqxhr, status, error) {
-            var errhtml;
-            if (jqxhr.getResponseHeader('Content-Type').match(/\btext\/html\b/)) {
-                var $response = $(jqxhr.responseText);
-                var $wrapper = $('div#page-wrapper', $response);
-                if ($wrapper.length) {
-                    errhtml = $wrapper.html();
+    }
+
+    var throttle = $(e.target).attr('data-load-throttle');
+    if (throttle) {
+        var now = (new Date()).getTime();
+        var loaded_at = $(e.target).attr('data-loaded-at');
+        if (loaded_at && (now - loaded_at) < throttle) {
+            setTimeout(function() {
+                $(e.target).trigger('arv:pane:reload');
+            });
+            return;
+        }
+    }
+
+    var $pane = $($(e.target).attr('href'));
+    if ($pane.hasClass('active')) {
+        console.log(e.target.id + " loading");
+
+        $(e.target).removeClass('pane-loaded');
+        $(e.target).removeClass('pane-stale');
+        $(e.target).addClass('pane-loading');
+
+        var content_url = $(e.target).attr('data-pane-content-url');
+        $.ajax(content_url, {dataType: 'html', type: 'GET', context: $pane}).
+            done(function(data, status, jqxhr) {
+                // Preserve collapsed state
+                var collapsable = {};
+                $(".collapse", $pane).each(function(i, c) {
+                    collapsable[c.id] = $(c).hasClass('in');
+                });
+                var tmp = $(data);
+                $(".collapse", tmp).each(function(i, c) {
+                    if (collapsable[c.id]) {
+                        $(c).addClass('in');
+                    } else {
+                        $(c).removeClass('in');
+                    }
+                });
+                $pane.html(tmp);
+                $(e.target).removeClass('pane-loading');
+                $(e.target).addClass('pane-loaded');
+                $(e.target).attr('data-loaded-at', (new Date()).getTime());
+                $pane.trigger('arv:pane:loaded');
+
+                console.log(e.target.id + " loaded");
+
+                if ($(e.target).hasClass('pane-stale')) {
+                    $(e.target).trigger('arv:pane:reload');
+                }
+            }).fail(function(jqxhr, status, error) {
+                var errhtml;
+                if (jqxhr.getResponseHeader('Content-Type').match(/\btext\/html\b/)) {
+                    var $response = $(jqxhr.responseText);
+                    var $wrapper = $('div#page-wrapper', $response);
+                    if ($wrapper.length) {
+                        errhtml = $wrapper.html();
+                    } else {
+                        errhtml = jqxhr.responseText;
+                    }
                 } else {
-                    errhtml = jqxhr.responseText;
+                    errhtml = ("An error occurred: " +
+                               (jqxhr.responseText || status)).
+                        replace(/&/g, '&amp;').
+                        replace(/</g, '&lt;').
+                        replace(/>/g, '&gt;');
                 }
-            } else {
-                errhtml = ("An error occurred: " +
-                           (jqxhr.responseText || status)).
-                    replace(/&/g, '&amp;').
-                    replace(/</g, '&lt;').
-                    replace(/>/g, '&gt;');
-            }
-            $('> div > div', this).html(
-                '<div><p>' +
-                    '<a href="#" class="btn btn-primary tab_reload">' +
-                    '<i class="fa fa-fw fa-refresh"></i> ' +
-                    'Reload tab</a></p><iframe></iframe></div>');
-            $('.tab_reload', this).click(function() {
-                $('> div > div', $pane).html(
-                    '<div class="spinner spinner-32px spinner-h-center"></div>');
-                $pane.trigger('arv:pane:reload');
+                $pane.html('<div><p>' +
+                        '<a href="#" class="btn btn-primary tab_reload">' +
+                        '<i class="fa fa-fw fa-refresh"></i> ' +
+                        'Reload tab</a></p><iframe></iframe></div>');
+                $('.tab_reload', $pane).click(function() {
+                    $pane.html('<div class="spinner spinner-32px spinner-h-center"></div>');
+                    $(e.target).trigger('arv:pane:reload');
+                });
+                // We want to render the error in an iframe, in order to
+                // avoid conflicts with the main page's element ids, etc.
+                // In order to do that dynamically, we have to set a
+                // timeout on the iframe window to load our HTML *after*
+                // the default source (e.g., about:blank) has loaded.
+                var iframe = $('iframe', e.target)[0];
+                iframe.contentWindow.setTimeout(function() {
+                    $('body', iframe.contentDocument).html(errhtml);
+                    iframe.height = iframe.contentDocument.body.scrollHeight + "px";
+                }, 1);
+                $(e.target).addClass('pane-loaded');
             });
-            // We want to render the error in an iframe, in order to
-            // avoid conflicts with the main page's element ids, etc.
-            // In order to do that dynamically, we have to set a
-            // timeout on the iframe window to load our HTML *after*
-            // the default source (e.g., about:blank) has loaded.
-            var iframe = $('iframe', this)[0];
-            iframe.contentWindow.setTimeout(function() {
-                $('body', iframe.contentDocument).html(errhtml);
-                iframe.height = iframe.contentDocument.body.scrollHeight + "px";
-            }, 1);
-            $(this).addClass('loaded');
-        });
-});
-
-// Fire when the content in a tab pane becomes stale/dirty. If the
-// pane is visible now, reload it right away. Otherwise, just replace
-// the current content with a spinner for now: there's no need to load
-// the new content unless/until the user clicks the corresponding tab.
-$(document).on('arv:pane:reload', '.tab-pane', function() {
-    // Unload a single pane. Reload it if it's active.
-    $(this).removeClass('loaded');
-    if ($(this).hasClass('active')) {
-        $('[href=#' + $(this).attr('id') + ']').trigger('shown.bs.tab');
     } else {
-        // When the user selects this tab, show a spinner instead of
+        console.log($(e.target).attr('href') + " is not active");
+        // When the user selects e.target tab, show a spinner instead of
         // old content while loading.
-        $('> div > div', this).
-            html('<div class="spinner spinner-32px spinner-h-center"></div>');
+        $(e.target).removeClass('pane-loading');
+        $(e.target).removeClass('pane-loaded');
+        $(e.target).removeClass('pane-stale');
+
+        $pane.html('<div class="spinner spinner-32px spinner-h-center"></div>');
     }
 });
 
-// Mark all panes as stale/dirty. Refresh the active pane.
-$(document).on('arv-log-event arv:pane:reload:all', function() {
-    $('.tab-pane.loaded').trigger('arv:pane:reload');
+// Mark all panes as stale/dirty. Refresh any 'active' panes.
+$(document).on('arv:pane:reload:all', function() {
+    $('.pane-anchor').trigger('arv:pane:reload');
+});
+
+$(document).on('ready ajax:complete', function() {
+    // Panes marked arv-refresh-on-log-event should be refreshed
+    $('.pane-anchor.arv-refresh-on-log-event').on('arv-log-event', function(e) {
+        $(e.target).trigger('arv:pane:reload');
+    });
+});
+
+// If there is a 'tab counts url' in the nav-tabs element then use it to get some javascript that will update them
+$(document).on('ready count-change', function() {
+    var tabCountsUrl = $('ul.nav-tabs').data('tab-counts-url');
+    if( tabCountsUrl && tabCountsUrl.length ) {
+        $.get( tabCountsUrl );
+    }
 });