4084: Fix workbench tests.
[arvados.git] / apps / workbench / app / assets / javascripts / tab_panes.js
1 // Load tab panes on demand. See app/views/application/_content.html.erb
2
3 // Fire when a tab is selected/clicked.
4 $(document).on('shown.bs.tab', '[data-toggle="tab"]', function(event) {
5     // When we switch tabs, remove "active" from any refreshable panes within
6     // the previous tab content so they don't continue to refresh unnecessarily, and
7     // add "active" to any refreshable panes under the newly shown tab content.
8
9     var tgt = $($(event.relatedTarget).attr('href'));
10     $(".pane-anchor", tgt).each(function (i, e) {
11         var a = $($(e).attr('href'));
12         a.removeClass("active");
13     });
14
15     tgt = $($(event.target).attr('href'));
16     $(".pane-anchor", tgt).each(function (i, e) {
17         var a = $($(e).attr('href'));
18         a.addClass("active");
19     });
20
21     // Now trigger reload of the newly shown tab pane.
22     $(event.target).trigger('arv:pane:reload');
23 });
24
25 // Ask a refreshable pane to reload via ajax.
26 // Target of this event is the anchoring element that manages the pane.
27 //
28 // Panes can be in one of three primary states, managed by setting CSS classes
29 // on the object: not loaded (no pane-* state classes), pane-loading, pane-loaded
30 //
31 // not loaded means the pane needs to be loaded when the pane becomes active
32 //
33 // pane-loading means there is a current AJAX call outstanding to reload the pane
34 //
35 // pane-loaded means the pane is believe to be up to date
36 //
37 // There are two additional states: pane-stale and pane-reload-pending
38 //
39 // pane-stale indicates a pane that is already loading has been invalidated and
40 // should schedule a reload immediately when the current load completes.  (This
41 // happens if there are clusters of events, where the reload is trigged by the
42 // first event, but we actually want to display the state after the final event
43 // has been processed.)
44 //
45 // pane-reload-pending indicates a reload is scheduled, to suppress
46 // scheduling any additional reloads.
47 $(document).on('arv:pane:reload', function(e) {
48     e.stopPropagation();
49
50     // '$anchor' is the event target, which is a .pane-anchor or a bootstrap
51     // tab anchor.  This is the element that stores the state of the pane.  The
52     // actual element that will contain the content is pointed to in the 'href'
53     // attribute of etarget.
54     var $anchor = $(e.target);
55
56     if ($anchor.hasClass('pane-loading')) {
57         // Already loading, mark stale to schedule a reload after this one.
58         $anchor.addClass('pane-stale');
59         return;
60     }
61
62     if ($anchor.hasClass('pane-no-auto-reload') && $anchor.hasClass('pane-loaded')) {
63         // Have to explicitly remove pane-loaded if we want it to reload.
64         return;
65     }
66
67     var throttle = $anchor.attr('data-load-throttle');
68     if (!throttle) {
69         throttle = 3000;
70     }
71     var now = (new Date()).getTime();
72     var loaded_at = $anchor.attr('data-loaded-at');
73     var since_last_load = now - loaded_at;
74     if (loaded_at && (since_last_load < throttle)) {
75         if (!$anchor.hasClass('pane-reload-pending')) {
76             $anchor.addClass('pane-reload-pending');
77             setTimeout((function() {
78                 $anchor.trigger('arv:pane:reload');
79             }), throttle - since_last_load);
80         }
81         return;
82     }
83
84     // We know this doesn't have 'pane-loading' because we tested for it above
85     $anchor.removeClass('pane-reload-pending');
86     $anchor.removeClass('pane-loaded');
87     $anchor.removeClass('pane-stale');
88
89     // $pane is the actual content area that is going to be updated.
90     var $pane = $($anchor.attr('href'));
91     if ($pane.hasClass('active')) {
92         $anchor.addClass('pane-loading');
93
94         var content_url = $anchor.attr('data-pane-content-url');
95         $.ajax(content_url, {dataType: 'html', type: 'GET', context: $pane}).
96             done(function(data, status, jqxhr) {
97                 // Preserve collapsed state
98                 var collapsable = {};
99                 $(".collapse", $(this)).each(function(i, c) {
100                     collapsable[c.id] = $(c).hasClass('in');
101                 });
102                 var tmp = $(data);
103                 $(".collapse", tmp).each(function(i, c) {
104                     if (collapsable[c.id]) {
105                         $(c).addClass('in');
106                     } else {
107                         $(c).removeClass('in');
108                     }
109                 });
110                 $(this).html(tmp);
111                 $anchor.removeClass('pane-loading');
112                 $anchor.addClass('pane-loaded');
113                 $anchor.attr('data-loaded-at', (new Date()).getTime());
114                 $(this).trigger('arv:pane:loaded');
115
116                 if ($anchor.hasClass('pane-stale')) {
117                     $anchor.trigger('arv:pane:reload');
118                 }
119             }).fail(function(jqxhr, status, error) {
120                 var errhtml;
121                 if (jqxhr.getResponseHeader('Content-Type').match(/\btext\/html\b/)) {
122                     var $response = $(jqxhr.responseText);
123                     var $wrapper = $('div#page-wrapper', $response);
124                     if ($wrapper.length) {
125                         errhtml = $wrapper.html();
126                     } else {
127                         errhtml = jqxhr.responseText;
128                     }
129                 } else {
130                     errhtml = ("An error occurred: " +
131                                (jqxhr.responseText || status)).
132                         replace(/&/g, '&amp;').
133                         replace(/</g, '&lt;').
134                         replace(/>/g, '&gt;');
135                 }
136                 $(this).html('<div><p>' +
137                         '<a href="#" class="btn btn-primary tab_reload">' +
138                         '<i class="fa fa-fw fa-refresh"></i> ' +
139                         'Reload tab</a></p><iframe style="width: 100%"></iframe></div>');
140                 $('.tab_reload', $(this)).click(function() {
141                     $(this).html('<div class="spinner spinner-32px spinner-h-center"></div>');
142                     $anchor.trigger('arv:pane:reload');
143                 });
144                 // We want to render the error in an iframe, in order to
145                 // avoid conflicts with the main page's element ids, etc.
146                 // In order to do that dynamically, we have to set a
147                 // timeout on the iframe window to load our HTML *after*
148                 // the default source (e.g., about:blank) has loaded.
149                 var iframe = $('iframe', $(this))[0];
150                 iframe.contentWindow.setTimeout(function() {
151                     $('body', iframe.contentDocument).html(errhtml);
152                     iframe.height = iframe.contentDocument.body.scrollHeight + "px";
153                 }, 1);
154                 $anchor.addClass('pane-loaded');
155             });
156     } else {
157         // When the user selects e.target tab, show a spinner instead of
158         // old content while loading.
159         $pane.html('<div class="spinner spinner-32px spinner-h-center"></div>');
160     }
161 });
162
163 // Mark all panes as stale/dirty. Refresh any 'active' panes.
164 $(document).on('arv:pane:reload:all', function() {
165     $('.pane-anchor').trigger('arv:pane:reload');
166 });
167
168 $(document).on('ready ajax:complete', function() {
169     // Panes marked arv-refresh-on-log-event should be refreshed
170     $('.pane-anchor.arv-refresh-on-log-event').on('arv-log-event', function(e) {
171         $(e.target).trigger('arv:pane:reload');
172     });
173 });
174
175 // If there is a 'tab counts url' in the nav-tabs element then use it to get some javascript that will update them
176 $(document).on('ready count-change', function() {
177     var tabCountsUrl = $('ul.nav-tabs').data('tab-counts-url');
178     if( tabCountsUrl && tabCountsUrl.length ) {
179         $.get( tabCountsUrl );
180     }
181 });