[ARVADOS] updated: 270b1fc3ec06a24057301a1e49d1f46b6fed3ebe

git at public.curoverse.com git at public.curoverse.com
Thu Nov 6 15:00:51 EST 2014


Summary of changes:
 apps/workbench/Gemfile.lock                        |   8 +-
 .../app/assets/javascripts/application.js          |   4 +
 apps/workbench/app/assets/javascripts/event_log.js |  66 ++---
 .../workbench/app/assets/javascripts/filterable.js |  51 ++++
 .../app/assets/javascripts/infinite_scroll.js      |  75 +++++
 .../app/assets/javascripts/pipeline_instances.js   |  46 +++-
 apps/workbench/app/assets/javascripts/tab_panes.js | 267 +++++++++---------
 .../app/assets/stylesheets/application.css.scss    |   8 +
 .../app/controllers/application_controller.rb      |  22 +-
 apps/workbench/app/controllers/jobs_controller.rb  |  20 +-
 .../controllers/pipeline_instances_controller.rb   |  79 ++++--
 .../app/controllers/projects_controller.rb         |  29 +-
 apps/workbench/app/helpers/provenance_helper.rb    | 305 ++++++++++-----------
 apps/workbench/app/models/arvados_api_client.rb    |   2 +-
 apps/workbench/app/models/pipeline_instance.rb     |   6 +-
 .../app/views/application/_content.html.erb        |  22 +-
 .../app/views/collections/_show_files.html.erb     |  33 ++-
 .../app/views/jobs/_show_job_component.html.erb    |   6 -
 .../workbench/app/views/jobs/_show_status.html.erb |  60 ++--
 apps/workbench/app/views/jobs/show.html.erb        |  11 +-
 .../pipeline_instances/_running_component.html.erb |   2 +-
 .../pipeline_instances/_show_components.html.erb   |  21 +-
 .../views/pipeline_instances/_show_log.html.erb    |  12 +-
 .../app/views/pipeline_instances/show.html.erb     |   7 +-
 .../views/projects/_show_data_collections.html.erb |   3 +-
 .../projects/_show_jobs_and_pipelines.html.erb     |   4 +-
 .../views/projects/_show_other_objects.html.erb    |   3 +-
 .../projects/_show_pipeline_templates.html.erb     |   3 +-
 .../app/views/projects/_show_subprojects.html.erb  |   3 +-
 .../app/views/projects/_show_tab_contents.html.erb |  17 +-
 apps/workbench/app/views/projects/index.html.erb   |   8 +-
 apps/workbench/config/application.yml.example      |   9 +
 .../config/environments/production.rb.example      |   2 +-
 .../controllers/collections_controller_test.rb     |  69 +++++
 .../pipeline_instances_controller_test.rb          | 169 ++++++++++++
 .../workbench/test/integration/collections_test.rb |  39 +++
 .../test/integration/pipeline_instances_test.rb    |   6 +-
 apps/workbench/test/integration/projects_test.rb   | 204 +++++++-------
 apps/workbench/test/integration/websockets_test.rb |  34 +--
 apps/workbench/test/test_helper.rb                 |  15 +
 apps/workbench/test/unit/pipeline_instance_test.rb |  42 ++-
 doc/_config.yml                                    |  11 +-
 .../create-standard-objects.html.textile.liquid    |  62 ++---
 doc/install/index.html.textile.liquid              |  21 +-
 doc/install/install-api-server.html.textile.liquid | 142 +++++-----
 .../install-crunch-dispatch.html.textile.liquid    |   8 +-
 doc/install/install-keep.html.textile.liquid       |  54 ----
 doc/install/install-keepproxy.html.textile.liquid  |  84 ++++++
 doc/install/install-keepstore.html.textile.liquid  |  90 ++++++
 .../install-manual-overview.html.textile.liquid    |  16 ++
 ...l-manual-prerequisites-ruby.html.textile.liquid |  31 +++
 ...nstall-manual-prerequisites.html.textile.liquid |  46 ++++
 .../install-shell-server.html.textile.liquid       |  17 ++
 doc/install/install-sso.html.textile.liquid        |   9 +-
 .../install-workbench-app.html.textile.liquid      | 106 ++++---
 docker/api/Dockerfile                              |   9 +-
 docker/api/application.yml.in                      |   2 +
 docker/arvdock                                     |  18 +-
 docker/base/Dockerfile                             |  15 +-
 docker/compute/Dockerfile                          |  11 +-
 docker/compute/supervisor.conf                     |   5 +
 docker/doc/Dockerfile                              |   4 +-
 docker/java-bwa-samtools/Dockerfile                |   4 +-
 docker/passenger/Dockerfile                        |  13 +-
 docker/shell/Dockerfile                            |   7 +-
 docker/slurm/Dockerfile                            |   3 +-
 docker/workbench/Dockerfile                        |   3 +-
 sdk/cli/arvados-cli.gemspec                        |   2 +-
 sdk/cli/bin/arv                                    |   4 +-
 sdk/cli/bin/arv-tag                                |   4 +-
 sdk/cli/bin/crunch-job                             |  99 ++++---
 sdk/python/arvados/collection.py                   | 208 ++++++++------
 sdk/python/arvados/commands/put.py                 |  49 ++--
 sdk/python/arvados/config.py                       |   2 +-
 sdk/python/arvados/keep.py                         |   2 +-
 sdk/python/arvados/stream.py                       |   4 +-
 sdk/python/arvados/util.py                         |  29 +-
 sdk/python/bin/arv-get                             |   2 +
 sdk/python/bin/arv-normalize                       |   1 +
 sdk/python/tests/run_test_server.py                |   8 +-
 sdk/python/tests/test_collections.py               | 162 ++++++++---
 sdk/ruby/arvados.gemspec                           |   2 +-
 sdk/ruby/lib/arvados.rb                            |   2 +-
 sdk/ruby/lib/arvados/keep.rb                       |   1 +
 sdk/ruby/test/test_keep_manifest.rb                |  22 +-
 .../arvados/v1/collections_controller.rb           |  53 +++-
 .../controllers/arvados/v1/groups_controller.rb    |  14 +-
 services/api/app/models/user.rb                    |   2 +
 services/api/config/application.yml.example        |   1 +
 services/api/lib/load_param.rb                     |  16 +-
 .../test/fixtures/api_client_authorizations.yml    |   7 +
 services/api/test/fixtures/collections.yml         |  61 ++++-
 services/api/test/fixtures/groups.yml              |  16 +-
 services/api/test/fixtures/jobs.yml                |  45 ++-
 services/api/test/fixtures/links.yml               |  15 +
 services/api/test/fixtures/pipeline_instances.yml  |  51 +++-
 services/api/test/fixtures/pipeline_templates.yml  |  21 ++
 services/api/test/fixtures/users.yml               |  14 +
 .../arvados/v1/groups_controller_test.rb           |  32 +++
 services/fuse/arvados_fuse/__init__.py             |   1 +
 services/fuse/setup.py                             |   2 +-
 services/fuse/tests/test_mount.py                  |  67 ++---
 services/nodemanager/tests/test_computenode.py     |   3 +-
 103 files changed, 2481 insertions(+), 1126 deletions(-)
 delete mode 100644 apps/workbench/app/views/jobs/_show_job_component.html.erb
 create mode 100644 apps/workbench/test/controllers/collections_controller_test.rb
 delete mode 100644 doc/install/install-keep.html.textile.liquid
 create mode 100644 doc/install/install-keepproxy.html.textile.liquid
 create mode 100644 doc/install/install-keepstore.html.textile.liquid
 create mode 100644 doc/install/install-manual-overview.html.textile.liquid
 create mode 100644 doc/install/install-manual-prerequisites-ruby.html.textile.liquid
 create mode 100644 doc/install/install-manual-prerequisites.html.textile.liquid
 create mode 100644 doc/install/install-shell-server.html.textile.liquid

       via  270b1fc3ec06a24057301a1e49d1f46b6fed3ebe (commit)
       via  f9a218aa75b9b88632d19d3c33c9c88c5fe39496 (commit)
       via  52083e6a73c6b9e46b5e4e176aec6b23bcec74e8 (commit)
       via  84cfa5256d2e8caa6ceea8dd274f5c4b2c96cd87 (commit)
       via  3981c82b166c2b1475d703820fddf41303a04c39 (commit)
       via  1c41009911eb63c4b90f600ca851f0cf961003e1 (commit)
       via  6862544ea5ff9272c537672a7a9cc6c98d0192fe (commit)
       via  95d1231a25637c5ba0fd07b116876b17711ec201 (commit)
       via  f075cec933827186d93d5d16ab5ab6803b31d40d (commit)
       via  0f834e80149820f3300b32072526e6f702493433 (commit)
       via  24d3d682fc97e24ab06e1335953b140b2e49547f (commit)
       via  5257d99306e915cc9bb5a65eca81f8517e6e222d (commit)
       via  d5cf000ee76060ce5e96bb4032f54e7f6b19116e (commit)
       via  cbb6e930023aeffdd03332894c85bc156316c72f (commit)
       via  d65b1921a9aeffd6906b95aa927a07a48f013b32 (commit)
       via  c54353188145d882218997a4d746c8b091a070e7 (commit)
       via  94e9d86f85c808e905269cde830341823b1c614b (commit)
       via  e0a85f879dfa5ffcb2713e10b62ae49662c10878 (commit)
       via  850bc55e74beb1d444bf518ce0be9cb0eacaa158 (commit)
       via  db2411a5e8b0dab503a4f712e14c65ef33947a62 (commit)
       via  963f0bf8fc030052aea9a7fcd89ca6ef88967c96 (commit)
       via  68dae835a2f8bebd5664211a60fc76d50e5ecc23 (commit)
       via  3e5e8972f4216a90b5bd17dff4a1c5f22106f820 (commit)
       via  a70e6990cd6903cd30b1c54b7454fffc1848e63c (commit)
       via  b798b325087c82578b4b079df968ad844a93dfb3 (commit)
       via  b7ec383316ca68220f9f54fe1602766284854173 (commit)
       via  5dd7bd8a05d52838b1cb8df7b341a843abae7a0a (commit)
       via  15dbb3110b3b7cc6712e58bdfbb9a5df7fab1882 (commit)
       via  411305d88273e22f286a127d43b88cc33681d9ff (commit)
       via  47c349ca374356163d15e59deb917a4d22f88206 (commit)
       via  3978e8ae9c1b5bbc07c63fd062e189a81e52399c (commit)
       via  ab3b9556865e694a4c33ed623cf2e55fc410f10f (commit)
       via  3b408ec4031c84549383db1ca99364e4c53a29fd (commit)
       via  6c7e0599e34d037c0e280b7622969488fb8f8b2e (commit)
       via  ae96829cc088d18c7aaf438eacb75830c7a40662 (commit)
       via  a6606b79fb6496e43eef6b2e8b04d5c1061f5635 (commit)
       via  4498d81399482be11a00e5e2223ef08d0576ef06 (commit)
       via  28053f8adcf1c36ba0a8cdf9c0b348765e68591a (commit)
       via  2a482395964360260020bf70e7645b08b106b1f1 (commit)
       via  ce2362b3b62ccb8fe2e5db5a2b87e9a6212ed86b (commit)
       via  55a0e060233385a9be029d0e75c152307950e268 (commit)
       via  83acfa97bdd34983d284b199e0c67a411aa7580e (commit)
       via  017587d701e08b38d9468dce45e121a69ecd8135 (commit)
       via  f9328bf507490f48e57b196be1de55d946644dea (commit)
       via  7505be2bb39b0a96304c46e89cd5392c8c037215 (commit)
       via  313f5fedd4214d077e2b5c7c26bab4df3895c44a (commit)
       via  fad871168f4500959d48eac695336e49a92535b7 (commit)
       via  3f9d8b5ca7288921a62f5cbc6c59d64c4fd16707 (commit)
       via  f39bdf87343e1e8a2c5c81221d262422c2651de2 (commit)
       via  102582e4b0c70349fc6b9077fd7618782a1a49bd (commit)
       via  27b534ddd7d2b56ff63cd2ca100fa3119df51a2f (commit)
       via  4978c1e740700ec78c26a8046a457496fd9b974c (commit)
       via  9ac37f367da0b11971f2ecf8a80a38ae60d43a61 (commit)
       via  afc70738e8dbbc96e177f1f920b1e8e5f2598fa9 (commit)
       via  28ac1f93305968a913c394dd3d3581c4e290722d (commit)
       via  7c3f2671d43770240a834b9bd5c34ec748acdc1d (commit)
       via  d14c0c8186517176ecf182cf2555aee8ba4ede6d (commit)
       via  ed4105d0c6a0d453143849afcea33960afc22117 (commit)
       via  9fa70c844ccc36a8c9543a8fafd2b56388c7a957 (commit)
       via  4d195ee9ed6a0d05dbdc31c40b6e899a49cb77ec (commit)
       via  b28531cd8051a2c9ae794ac2d3a2b7140e357407 (commit)
       via  e3e54264e8bc767e1ec773cff4e5bdf4c4934a36 (commit)
       via  c6a03a7abff947dc8242e8be18b4b5e6920a3e4a (commit)
       via  4cbb39443fdd218773b4d192330e5e3632fdaaf7 (commit)
       via  b39e2b4e3f791d0c0afe9682183bd31d8341cea3 (commit)
       via  6dabd04bf356cccdb2a7d366a6edd2146b90519a (commit)
       via  92822dd390f4b3efeb183452b7ddf866c41bda17 (commit)
       via  a95a81202b12a79467018157afe54ed09deb9bda (commit)
       via  0e8d9b4c01c08f0efbccd91497177fc102aa5c25 (commit)
       via  c199c0cf4a2fe22ce3e1dd0e6219937588711c50 (commit)
       via  ce6f044cfd55f761ec37112554aeab578b3ab476 (commit)
       via  1bcfc05de27282f239de4124225e27d05a1d149a (commit)
       via  fd0ca9211847806adb2e97c0ae78e9312ef89ca1 (commit)
       via  9b959a512c8cca0df65b2bf36ca4baf8eb7a809b (commit)
       via  da6674e4560229457d45abbb8a8aff9d4e305459 (commit)
       via  51e79a3be745cfd37efc49471ae21a1d54662e7f (commit)
       via  1bcf9398f365411b2a82388dbd2eb1fd1d570fe5 (commit)
       via  1d2cdef7174db30cb926d42e321ccb8957d890f2 (commit)
       via  52141835e85e4014a691e036f89bcd71a98bd6b2 (commit)
       via  e2fe6c0e5c1c62a37e03519590c04a5186a2cc9b (commit)
       via  c105f3566ef117227dae65396425618c586d9e10 (commit)
       via  78fc6d80639659e30e4f15562c382c002ed6e1ef (commit)
       via  ddc1f6b0c54b72df395cfef26e0dc4eb8f65463d (commit)
       via  7d8d1b78a10a7045d7ef2367a45c07b023272548 (commit)
       via  9ee4a125d1796dd7b71ef1b9f255133e5cf0becc (commit)
       via  5560cdf37024ba98bb0367a65bc2176c1577496b (commit)
       via  c19bb2a3554f8bcb17cadd9c133b6fb260e70513 (commit)
       via  1edad4ad7a8c239b36b6f10565ee36cbeab67ddb (commit)
       via  c10b68e95cd648caade631ca9bdeceba8e707384 (commit)
       via  550997435f03118e51571994acc07273907bc6fd (commit)
       via  9f37a732c9bfb3892231765f37e213af4e037e68 (commit)
       via  9ad6d61c1576b604b936eb981683941078d79d8e (commit)
       via  89ba1124c9fd6d3167c50059dc8adea64e6147b5 (commit)
       via  f549c9f05860abfb394b550c0b2c30a5a7c33283 (commit)
       via  c4aefe62bd09b771c6e1cef7130645819af72cac (commit)
       via  4d257edde88edfb9530afa0d8b0ba7c9216cfb3a (commit)
       via  fb2ee4033abfae93e8f9a9af367569e7f4fa3793 (commit)
       via  cbc29982e30fd776c194c47dc584710ff1b340c4 (commit)
       via  e34135f6779f58852558e03b1b95534c11ca07ae (commit)
       via  81bb05b5a18c9501057876bf5b0e1923778dad26 (commit)
       via  f78b6578bc950b72f0ffac516bde9c8fde2416be (commit)
       via  d0e60c70302c1b7ec84110cad0545924605c95bb (commit)
       via  fb1bf9fde205adb90386c81214e667823708a9f1 (commit)
       via  2b6948ff7e11cd236ea26562ed76a534dcf3c0be (commit)
       via  8323e56e9dafb1bd4aee4f548c26854ea3eb05e9 (commit)
       via  112d6ce9132ea6749aef115e3787483958e858fd (commit)
       via  d9e2eb19dfef24c8b6af396b2b61b2dab4cf740d (commit)
       via  6817b34daf51030fd25459d349a940ff59eee690 (commit)
       via  6d23c29612aedb12e35930cf37a9c3b36839359b (commit)
       via  97f3db9cb084efce35ef6a24c25d14308785a49a (commit)
       via  e1020f4ad01b6f583a9d3c2bf6d146cd9e0d9331 (commit)
       via  337a0bdb43f518fbf716ed5ecd66eb5e569b8fdf (commit)
       via  c330d58918b7c631307ec11bd584824bf0da7f47 (commit)
       via  47853db92d6e1b9eca4beddf471189ee9f4e0217 (commit)
       via  30bdc9b4fd57e2291f2ace690412d1775eb6e6ab (commit)
       via  c79874f462520568509bd8a880b8dacab8036489 (commit)
       via  c0943b24a1deb33ef2d866e722f77bc4e564ead5 (commit)
       via  0f9aaf18f1931984dcb34920515877d74d362841 (commit)
       via  6bfa13079feb0cd6632dcf83dd6e1dc1d99d8cdf (commit)
       via  37b765276dbde3d50f403d269288929d3b211564 (commit)
       via  6ad6190f242a657e6df08014ad3abf4254bfda9b (commit)
       via  fd1613bf6ad5af029255959ecf86f313b08d03ee (commit)
       via  f7a0b41371ca4448d6c0e912cc5fede1869ba265 (commit)
       via  3a47eb915b3865bea6d2e6536c7bc1f4a1afc8b4 (commit)
       via  4e148ec9b5db231b22f8d9bd04527023fdbc23ba (commit)
       via  c5f31bd6f1484dc1d09ec404e201f69294f84a5b (commit)
       via  2c16f4fbf9408bb758a0f54cae4058dccef4c2a5 (commit)
       via  c1efd42bfbb5bc7ff9ac275643ee7a0c4e44012b (commit)
       via  66c13b6055a363cb08197b8c5d040ed9a511c8ca (commit)
       via  1bde97d8aa4d670c25015284e97281e1af163dda (commit)
       via  f8dab15791a8f19839f9db529a820b41f10440ae (commit)
       via  d501ef45757f78f5c1d2843eccf8016e8978345e (commit)
       via  4e624965e369180dfb971bf3a53ae2b4d726f271 (commit)
       via  70609f51a1124be1704f4a61aad9a597c961c0e8 (commit)
       via  b82eca689e7374cdafbb3435c4168579850649ac (commit)
       via  3103c676533ec17bed96cf38a264a2415534499a (commit)
       via  0c6b79271aa6cc8cf4f3b125a9d7a7f5645b027b (commit)
      from  f809f3edb39aa33ada41059c95367e6d5a84f2ba (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 270b1fc3ec06a24057301a1e49d1f46b6fed3ebe
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu Nov 6 11:12:58 2014 -0500

    4084: Fix wrong state name: jobs are Running, pipelines are RunningOn[...].

diff --git a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
index 82f18e7..7735997 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
@@ -2,7 +2,7 @@
 
   <%
      job_uuids = @object.components.map { |k,j| j.is_a? Hash and j[:job].andand[:uuid] }.compact
-     throttle = @object.state == 'Running' ? 5000 : 15000
+     throttle = @object.state.start_with?('Running') ? 5000 : 15000
      %>
   <div class="arv-log-refresh-control"
        data-load-throttle="<%= throttle %>"

commit f9a218aa75b9b88632d19d3c33c9c88c5fe39496
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu Nov 6 10:53:13 2014 -0500

    4084: Update views that were still using pane-anchor.

diff --git a/apps/workbench/app/views/jobs/_show_status.html.erb b/apps/workbench/app/views/jobs/_show_status.html.erb
index cfff0da..8075209 100644
--- a/apps/workbench/app/views/jobs/_show_status.html.erb
+++ b/apps/workbench/app/views/jobs/_show_status.html.erb
@@ -1,43 +1,36 @@
-<div style="margin-top: 10px">
+<div class="arv-log-refresh-control"
+     data-load-throttle="15000"
+     ></div>
+<%=
+   pj = {}
+   pj[:job] = @object
+   pj[:name] = @object[:name] || "this job"
+   pj[:progress_bar] = render(partial: "job_progress",
+                              locals: {:j => @object })
+   tasks = JobTask.filter([['job_uuid', '=', @object.uuid]]).results
+   render(partial: 'pipeline_instances/running_component',
+          locals: { tasks: tasks, pj: pj, i: 0, expanded: true})
+%>
 
-<div id="job-status"
-     class="pane-anchor arv-log-event-listener arv-refresh-on-log-event"
-     href="#job-status-pane"
-     data-pane-content-url="<%= url_for(params.merge(tab_pane: "job_component")) %>"
-     data-object-uuid="<%= @object.uuid %>"
-     data-load-throttle="5000"
-     >
-  <div id="job-status-pane">
-    <%=
-       pj = {}
-       pj[:job] = @object
-       pj[:name] = @object[:name] || "this job"
-       pj[:progress_bar] = render(partial: "job_progress",
-                                  locals: {:j => @object })
-       tasks = JobTask.filter([['job_uuid', '=', @object.uuid]]).results
-       render(partial: 'pipeline_instances/running_component',
-              locals: { tasks: tasks, pj: pj, i: 0, expanded: true})
-    %>
+<div class="panel panel-default">
+  <div class="panel-heading">
+    <span class="panel-title">Used in pipelines</span>
   </div>
-</div>
-</div>
-
-  <div class="panel panel-default">
-    <div class="panel-heading">
-      <span class="panel-title">Used in pipelines</span>
-    </div>
-    <div class="panel-body">
-<% pi = PipelineInstance.order("created_at desc").filter([["components", "like", "%#{@object.uuid}%"]]) %>
+  <div class="panel-body">
+    <% pi = PipelineInstance.order("created_at desc").filter([["components", "like", "%#{@object.uuid}%"]]) %>
 
-<% pi.each do |pipeline| %>
-  <% pipeline.components.each do |k, v| %>
-    <% if v[:job] and v[:job][:uuid] == @object.uuid %>
-      <div>
-      <b><%= k %></b> component of <%= link_to_if_arvados_object pipeline, friendly_name: true %>
-      created at <%= render_localized_date(pipeline.created_at) %>.
-      </div>
+    <% pi.each do |pipeline| %>
+      <% pipeline.components.each do |k, v| %>
+        <% if v[:job] and v[:job][:uuid] == @object.uuid %>
+          <div>
+            <b><%= k %></b>
+            component of
+            <%= link_to_if_arvados_object pipeline, friendly_name: true %>
+            created at
+            <%= render_localized_date(pipeline.created_at) %>.
+          </div>
+        <% end %>
+      <% end %>
     <% end %>
-  <% end %>
-<% end %>
-</div>
+  </div>
 </div>
diff --git a/apps/workbench/app/views/pipeline_instances/show.html.erb b/apps/workbench/app/views/pipeline_instances/show.html.erb
index 2b07352..860e809 100644
--- a/apps/workbench/app/views/pipeline_instances/show.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/show.html.erb
@@ -18,14 +18,11 @@
 <% content_for :tab_line_buttons do %>
 
   <div id="pipeline-instance-tab-buttons"
-       class="pane-anchor active arv-log-event-listener arv-refresh-on-state-change"
-       href="#pipeline-instance-tab-buttons-pane"
+       class="pane-loaded arv-log-event-listener arv-refresh-on-state-change"
        data-pane-content-url="<%= url_for(params.merge(tab_pane: "tab_buttons")) %>"
        data-object-uuid="<%= @object.uuid %>"
        >
-    <div id="pipeline-instance-tab-buttons-pane" class="active">
-      <%= render partial: 'show_tab_buttons', locals: {object: @object}%>
-    </div>
+    <%= render partial: 'show_tab_buttons', locals: {object: @object}%>
   </div>
 
 <% end %>

commit 52083e6a73c6b9e46b5e4e176aec6b23bcec74e8
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed Nov 5 03:52:58 2014 -0500

    4084: Add comment.

diff --git a/apps/workbench/app/views/pipeline_instances/_show_log.html.erb b/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
index 060a669..4bb7ab0 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
@@ -4,6 +4,9 @@
      class="arv-log-event-listener arv-log-event-handler-append-logs arv-log-event-subscribe-to-pipeline-job-uuids arv-job-log-window"
      data-object-uuids="<%= log_uuids.join(' ') %>"
      ><%= log_history.join("\n") %></div>
+
+<%# Applying a long throttle suppresses the auto-refresh of this
+    partial that would normally be triggered by arv-log-event. %>
 <div class="arv-log-refresh-control"
      data-load-throttle="86486400000" <%# 1001 nights %>
      ></div>

commit 84cfa5256d2e8caa6ceea8dd274f5c4b2c96cd87
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed Nov 5 03:16:33 2014 -0500

    4084: Fix dashboard refresh.

diff --git a/apps/workbench/app/views/projects/index.html.erb b/apps/workbench/app/views/projects/index.html.erb
index ea52024..dc70da8 100644
--- a/apps/workbench/app/views/projects/index.html.erb
+++ b/apps/workbench/app/views/projects/index.html.erb
@@ -1,11 +1,7 @@
-<div id="dashboard-content"
-     class="pane-anchor pane-loaded arv-log-event-listener arv-refresh-on-log-event"
-     href="#dashboard-content-pane"
+<div class="pane-loaded arv-log-event-listener arv-refresh-on-log-event"
      data-pane-content-url="<%= root_url tab_pane: "dashboard" %>"
      data-object-uuid="all"
      data-load-throttle="15000"
      >
-  <div id="dashboard-content-pane" class="active">
-    <%= render partial: 'show_dashboard' %>
-  </div>
+  <%= render partial: 'show_dashboard' %>
 </div>

commit 3981c82b166c2b1475d703820fddf41303a04c39
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed Nov 5 02:58:20 2014 -0500

    4084: Remove excess loaded class.

diff --git a/apps/workbench/app/views/application/_content.html.erb b/apps/workbench/app/views/application/_content.html.erb
index 7afc824..782a6af 100644
--- a/apps/workbench/app/views/application/_content.html.erb
+++ b/apps/workbench/app/views/application/_content.html.erb
@@ -22,7 +22,7 @@
     <% pane_list.each_with_index do |pane, i| %>
       <% pane_name = (pane.is_a?(Hash) ? pane[:name] : pane) %>
       <div id="<%= pane_name %>"
-           class="tab-pane fade <%= 'in active loaded pane-loaded' if i==0 %> arv-log-event-listener arv-refresh-on-log-event arv-log-event-subscribe-to-pipeline-job-uuids"
+           class="tab-pane fade <%= 'in active pane-loaded' if i==0 %> arv-log-event-listener arv-refresh-on-log-event arv-log-event-subscribe-to-pipeline-job-uuids"
            <% if controller.action_name == "index" %>
              data-object-kind="arvados#<%= ArvadosApiClient.class_kind controller.model_class %>"
            <% else %>

commit 1c41009911eb63c4b90f600ca851f0cf961003e1
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed Nov 5 02:17:56 2014 -0500

    4084: Update comments.

diff --git a/apps/workbench/app/assets/javascripts/tab_panes.js b/apps/workbench/app/assets/javascripts/tab_panes.js
index fc05fde..07e46fe 100644
--- a/apps/workbench/app/assets/javascripts/tab_panes.js
+++ b/apps/workbench/app/assets/javascripts/tab_panes.js
@@ -10,30 +10,33 @@ $(document).on('shown.bs.tab', '[data-toggle="tab"]', function(event) {
 
 // Ask a refreshable pane to reload via ajax.
 //
-// Target of this event is the anchor element that manages the pane.  A reload
-// consists of an AJAX call to load the "data-pane-content-url" and replace the
-// contents of the DOM node pointed to by "href".
+// Target of this event is the DOM element to be updated. A reload
+// consists of an AJAX call to load the "data-pane-content-url" and
+// replace the content of the target element with the retrieved HTML.
 //
-// There are four CSS classes set on the object to indicate its state:
+// There are four CSS classes set on the element to indicate its state:
 // pane-loading, pane-stale, pane-loaded, pane-reload-pending
 //
 // There are five states based on the presence or absence of css classes:
 //
-// 1. no pane-* states means the pane must be loaded when the pane becomes active
+// 1. Absence of any pane-* states means the pane is empty, and should
+// be loaded as soon as it becomes visible.
 //
-// 2. "pane-loading" means an AJAX call has been made to reload the pane and we are
-// waiting on a result
+// 2. "pane-loading" means an AJAX call has been made to reload the
+// pane and we are waiting on a result.
 //
-// 3. "pane-loading pane-stale" indicates a pane that is already loading has
-// been invalidated and should schedule a reload immediately when the current
-// load completes.  (This happens when there is a cluster of events, where the
-// reload is triggered by the first event, but we want ensure that we
-// eventually load the final quiescent state).
+// 3. "pane-loading pane-stale" means the pane is loading, but has
+// already been invalidated and should schedule a reload as soon as
+// possible after the current load completes. (This happens when there
+// is a cluster of events, where the reload is triggered by the first
+// event, but we want ensure that we eventually load the final
+// quiescent state).
 //
-// 4. "pane-loaded" means the pane is up to date
+// 4. "pane-loaded" means the pane is up to date.
 //
-// 5. "pane-loaded pane-reload-pending" indicates a reload is scheduled (but has
-// not started yet), suppressing scheduling of any further reloads.
+// 5. "pane-loaded pane-reload-pending" means a reload is needed, and
+// has been scheduled, but has not started because the pane's
+// minimum-time-between-reloads throttle has not yet been reached.
 //
 $(document).on('arv:pane:reload', '[data-pane-content-url]', function(e) {
     if (this != e.target) {

commit 6862544ea5ff9272c537672a7a9cc6c98d0192fe
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed Nov 5 02:02:37 2014 -0500

    4084: Ignore bubbling arv-log-event events, just process each once at the original target.

diff --git a/apps/workbench/app/assets/javascripts/pipeline_instances.js b/apps/workbench/app/assets/javascripts/pipeline_instances.js
index 15134cb..761477e 100644
--- a/apps/workbench/app/assets/javascripts/pipeline_instances.js
+++ b/apps/workbench/app/assets/javascripts/pipeline_instances.js
@@ -48,6 +48,10 @@ $(document).on('ready ajax:complete', function() {
 });
 
 $(document).on('arv-log-event', '.arv-refresh-on-state-change', function(event, eventData) {
+    if (this != event.target) {
+        // Not interested in events sent to child nodes.
+        return;
+    }
     if (eventData.event_type == "update" &&
         eventData.properties.old_attributes.state != eventData.properties.new_attributes.state)
     {
@@ -56,6 +60,10 @@ $(document).on('arv-log-event', '.arv-refresh-on-state-change', function(event,
 });
 
 $(document).on('arv-log-event', '.arv-log-event-subscribe-to-pipeline-job-uuids', function(event, eventData){
+    if (this != event.target) {
+        // Not interested in events sent to child nodes.
+        return;
+    }
     if (!((eventData.object_kind == 'arvados#pipelineInstance') &&
           (eventData.event_type == "create" ||
            eventData.event_type == "update") &&
@@ -83,6 +91,10 @@ $(document).on('ready ajax:success', function() {
 });
 
 $(document).on('arv-log-event', '.arv-log-event-handler-append-logs', function(event, eventData){
+    if (this != event.target) {
+        // Not interested in events sent to child nodes.
+        return;
+    }
     var wasatbottom = ($(this).scrollTop() + $(this).height() >= this.scrollHeight);
 
     if (eventData.event_type == "stderr" || eventData.event_type == "stdout") {
diff --git a/apps/workbench/app/assets/javascripts/tab_panes.js b/apps/workbench/app/assets/javascripts/tab_panes.js
index 6c58a57..fc05fde 100644
--- a/apps/workbench/app/assets/javascripts/tab_panes.js
+++ b/apps/workbench/app/assets/javascripts/tab_panes.js
@@ -36,20 +36,21 @@ $(document).on('shown.bs.tab', '[data-toggle="tab"]', function(event) {
 // not started yet), suppressing scheduling of any further reloads.
 //
 $(document).on('arv:pane:reload', '[data-pane-content-url]', function(e) {
-    // $pane, the event target, is an element whose content is to be
-    // replaced. Pseudoclasses on $pane (pane-loading, etc) encode the
-    // current loading state.
-    var $pane = $(e.target);
-
-    var content_url = $pane.attr('data-pane-content-url');
-    if (!content_url) {
-        // When reloadable elements are nested, we can receive
-        // arv:pane:reload events even though the selector in .on()
-        // does not match e.target. Ignore such events.
+    if (this != e.target) {
+        // An arv:pane:reload event was sent to an element (e.target)
+        // which happens to have an ancestor (this) matching the above
+        // '[data-pane-content-url]' selector. This happens because
+        // events bubble up the DOM on their way to document. However,
+        // here we only care about events delivered directly to _this_
+        // selected element (i.e., this==e.target), not ones delivered
+        // to its children. The event "e" is uninteresting here.
         return;
     }
 
-    e.stopPropagation();
+    // $pane, the event target, is an element whose content is to be
+    // replaced. Pseudoclasses on $pane (pane-loading, etc) encode the
+    // current loading state.
+    var $pane = $(this);
 
     if ($pane.hasClass('pane-loading')) {
         // Already loading, mark stale to schedule a reload after this one.
@@ -99,6 +100,7 @@ $(document).on('arv:pane:reload', '[data-pane-content-url]', function(e) {
 
     $pane.addClass('pane-loading');
 
+    var content_url = $pane.attr('data-pane-content-url');
     $.ajax(content_url, {dataType: 'html', type: 'GET', context: $pane}).
         done(function(data, status, jqxhr) {
             // Preserve collapsed state
@@ -174,9 +176,13 @@ $(document).on('arv:pane:reload:all', function() {
     $('[data-pane-content-url]').trigger('arv:pane:reload');
 });
 
-$(document).on('arv-log-event', '.arv-refresh-on-log-event', function(e) {
+$(document).on('arv-log-event', '.arv-refresh-on-log-event', function(event) {
+    if (this != event.target) {
+        // Not interested in events sent to child nodes.
+        return;
+    }
     // Panes marked arv-refresh-on-log-event should be refreshed
-    $(e.target).trigger('arv:pane:reload');
+    $(event.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

commit 95d1231a25637c5ba0fd07b116876b17711ec201
Merge: f075cec d5cf000
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Nov 4 23:28:22 2014 -0500

    4084: Merge branch 'master' into 4084-log-pane-refresh-TC
    
    Conflicts:
    	services/api/test/fixtures/jobs.yml

diff --cc apps/workbench/test/test_helper.rb
index d1d63e2,49b9c7f..ab2ac39
--- a/apps/workbench/test/test_helper.rb
+++ b/apps/workbench/test/test_helper.rb
@@@ -204,7 -190,21 +204,22 @@@ class ActionController::TestCas
    end
  end
  
+ # If it quacks like a duck, it must be a HTTP request object.
+ class RequestDuck
+   def self.host
+     "localhost"
+   end
+ 
+   def self.port
+     8080
+   end
+ 
+   def self.protocol
+     "http"
+   end
+ end
+ 
  if ENV["RAILS_ENV"].eql? 'test'
 -  ApiServerForTests.run
 +  ApiServerForTests.new.run
 +  ApiServerForTests.new.run ["--websockets"]
  end
diff --cc services/api/test/fixtures/jobs.yml
index 9026e59,ebcecf4..65b40d7
--- a/services/api/test/fixtures/jobs.yml
+++ b/services/api/test/fixtures/jobs.yml
@@@ -295,30 -297,39 +297,63 @@@ job_in_subproject
    script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
    state: Complete
  
 +running_will_be_completed:
 +  uuid: zzzzz-8i9sb-pshmckwoma9plh8
 +  owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
 +  cancelled_at: ~
 +  cancelled_by_user_uuid: ~
 +  cancelled_by_client_uuid: ~
 +  created_at: <%= 3.minute.ago.to_s(:db) %>
 +  started_at: <%= 3.minute.ago.to_s(:db) %>
 +  finished_at: ~
 +  script_version: 1de84a854e2b440dc53bf42f8548afa4c17da332
 +  running: true
 +  success: ~
 +  output: ~
 +  priority: 0
 +  log: ~
 +  is_locked_by_uuid: zzzzz-tpzed-d9tiejq69daie8f
 +  tasks_summary:
 +    failed: 0
 +    todo: 3
 +    running: 1
 +    done: 1
 +  runtime_constraints: {}
 +  state: Running
 +
+ graph_stage1:
+   uuid: zzzzz-8i9sb-graphstage10000
+   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
+   repository: foo
+   script: hash
+   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
+   state: Complete
+   output: fa7aeb5140e2848d39b416daeef4ffc5+45
+ 
+ graph_stage2:
+   uuid: zzzzz-8i9sb-graphstage20000
+   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
+   repository: foo
+   script: hash2
+   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
+   state: Complete
+   script_parameters:
+     input: fa7aeb5140e2848d39b416daeef4ffc5+45
+     input2: "stuff"
+   output: 65b17c95fdbc9800fc48acda4e9dcd0b+93
+ 
+ graph_stage3:
+   uuid: zzzzz-8i9sb-graphstage30000
+   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
+   repository: foo
+   script: hash2
+   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
+   state: Complete
+   script_parameters:
+     input: fa7aeb5140e2848d39b416daeef4ffc5+45
+     input2: "stuff2"
+   output: ea10d51bcf88862dbcc36eb292017dfd+45
+ 
  
  # Test Helper trims the rest of the file
  

commit f075cec933827186d93d5d16ab5ab6803b31d40d
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Nov 4 23:20:23 2014 -0500

    4084: Use assert_text, not assert page_has_text?, to make tests reliable.

diff --git a/apps/workbench/Gemfile.lock b/apps/workbench/Gemfile.lock
index 1e882a9..ad1cf78 100644
--- a/apps/workbench/Gemfile.lock
+++ b/apps/workbench/Gemfile.lock
@@ -62,7 +62,7 @@ GEM
       net-sftp (>= 2.0.0)
       net-ssh (>= 2.0.14)
       net-ssh-gateway (>= 1.1.0)
-    capybara (2.2.1)
+    capybara (2.4.4)
       mime-types (>= 1.16)
       nokogiri (>= 1.3.3)
       rack (>= 1.0.0)
@@ -121,7 +121,7 @@ GEM
       treetop (~> 1.4.8)
     metaclass (0.0.4)
     mime-types (1.25.1)
-    mini_portile (0.5.2)
+    mini_portile (0.6.0)
     minitest (5.3.3)
     mocha (1.1.0)
       metaclass (~> 0.0.1)
@@ -134,8 +134,8 @@ GEM
     net-ssh (2.7.0)
     net-ssh-gateway (1.2.0)
       net-ssh (>= 2.6.5)
-    nokogiri (1.6.1)
-      mini_portile (~> 0.5.0)
+    nokogiri (1.6.3.1)
+      mini_portile (= 0.6.0)
     oj (2.1.7)
     passenger (4.0.23)
       daemon_controller (>= 1.1.0)
diff --git a/apps/workbench/test/integration/websockets_test.rb b/apps/workbench/test/integration/websockets_test.rb
index 7211adb..65784af 100644
--- a/apps/workbench/test/integration/websockets_test.rb
+++ b/apps/workbench/test/integration/websockets_test.rb
@@ -14,13 +14,13 @@ class WebsocketTest < ActionDispatch::IntegrationTest
     visit(page_with_token("admin", "/websockets"))
     fill_in("websocket-message-content", :with => "Stuff")
     click_button("Send")
-    assert page.has_text? '"status":400'
+    assert_text '"status":400'
   end
 
   test "test live logging" do
     visit(page_with_token("admin", "/pipeline_instances/zzzzz-d1hrv-9fm8l10i9z2kqc6"))
     click_link("Log")
-    assert page.has_no_text? '123 hello'
+    assert_no_text '123 hello'
 
     api = ArvadosApiClient.new
 
@@ -29,14 +29,14 @@ class WebsocketTest < ActionDispatch::IntegrationTest
                 object_uuid: "zzzzz-d1hrv-9fm8l10i9z2kqc6",
                 event_type: "stderr",
                 properties: {"text" => "123 hello"}}})
-    assert page.has_text? '123 hello'
+    assert_text '123 hello'
     Thread.current[:arvados_api_token] = nil
   end
 
   test "test live logging scrolling" do
     visit(page_with_token("admin", "/pipeline_instances/zzzzz-d1hrv-9fm8l10i9z2kqc6"))
     click_link("Log")
-    assert page.has_no_text? '123 hello'
+    assert_no_text '123 hello'
 
     api = ArvadosApiClient.new
 
@@ -50,7 +50,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
                 object_uuid: "zzzzz-d1hrv-9fm8l10i9z2kqc6",
                 event_type: "stderr",
                 properties: {"text" => text}}})
-    assert page.has_text? '1000 hello'
+    assert_text '1000 hello'
 
     # First test that when we're already at the bottom of the page, it scrolls down
     # when a new line is added.
@@ -60,7 +60,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
                 object_uuid: "zzzzz-d1hrv-9fm8l10i9z2kqc6",
                 event_type: "stderr",
                 properties: {"text" => "1001 hello\n"}}})
-    assert page.has_text? '1001 hello'
+    assert_text '1001 hello'
 
     # Check that new value of scrollTop is greater than the old one
     assert page.evaluate_script("$('#pipeline_event_log_div').scrollTop()") > old_top
@@ -73,7 +73,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
                 object_uuid: "zzzzz-d1hrv-9fm8l10i9z2kqc6",
                 event_type: "stderr",
                 properties: {"text" => "1002 hello\n"}}})
-    assert page.has_text? '1002 hello'
+    assert_text '1002 hello'
 
     # Check that we haven't changed scroll position
     assert_equal 30, page.evaluate_script("$('#pipeline_event_log_div').scrollTop()")
@@ -95,17 +95,17 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     visit(page_with_token("admin", "/pipeline_instances/#{p.uuid}"))
 
-    assert page.has_text? 'Active'
+    assert_text 'Active'
     assert page.has_link? 'Pause'
-    assert page.has_no_text? 'Complete'
+    assert_no_text 'Complete'
     assert page.has_no_link? 'Re-run with latest'
 
     p.state = "Complete"
     p.save!
 
-    assert page.has_no_text? 'Active'
+    assert_no_text 'Active'
     assert page.has_no_link? 'Pause'
-    assert page.has_text? 'Complete'
+    assert_text 'Complete'
     assert page.has_link? 'Re-run with latest'
 
     Thread.current[:arvados_api_token] = nil
@@ -118,14 +118,14 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     visit(page_with_token("admin", "/jobs/#{p.uuid}"))
 
-    assert page.has_no_text? 'complete'
-    assert page.has_no_text? 'Re-run same version'
+    assert_no_text 'complete'
+    assert_no_text 'Re-run same version'
 
     p.state = "Complete"
     p.save!
 
-    assert page.has_text? 'complete'
-    assert page.has_text? 'Re-run same version'
+    assert_text 'complete'
+    assert_text 'Re-run same version'
 
     Thread.current[:arvados_api_token] = nil
   end
@@ -135,7 +135,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
 
     visit(page_with_token("admin", "/"))
 
-    assert page.has_no_text? 'test dashboard arv-refresh-on-log-event'
+    assert_no_text 'test dashboard arv-refresh-on-log-event'
 
     # Do something and check that the pane reloads.
     p = PipelineInstance.create({state: "RunningOnServer",
@@ -144,7 +144,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest
                                   }
                                 })
 
-    assert page.has_text? 'test dashboard arv-refresh-on-log-event'
+    assert_text 'test dashboard arv-refresh-on-log-event'
 
     Thread.current[:arvados_api_token] = nil
   end

commit 0f834e80149820f3300b32072526e6f702493433
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Nov 4 22:54:12 2014 -0500

    4084: Skip the "inactive pane" optimization when the div is not a tab pane.
    
    Content divs that are _not_ tabbed panes no longer need to remember to
    add an "active" flag.

diff --git a/apps/workbench/app/assets/javascripts/tab_panes.js b/apps/workbench/app/assets/javascripts/tab_panes.js
index 3f1645b..6c58a57 100644
--- a/apps/workbench/app/assets/javascripts/tab_panes.js
+++ b/apps/workbench/app/assets/javascripts/tab_panes.js
@@ -84,9 +84,15 @@ $(document).on('arv:pane:reload', '[data-pane-content-url]', function(e) {
     $pane.removeClass('pane-loaded');
     $pane.removeClass('pane-stale');
 
-    if (!$pane.hasClass('active')) {
-        // When the user selects e.target tab, show a spinner instead of
-        // old content while loading.
+    if (!$pane.hasClass('active') &&
+        $pane.parent().hasClass('tab-content')) {
+        // $pane is one of the content areas in a bootstrap tabs
+        // widget, and it isn't the currently selected tab. If and
+        // when the user does select the corresponding tab, it will
+        // get a shown.bs.tab event, which will invoke this reload
+        // function again (see handler above). For now, we just insert
+        // a spinner, which will be displayed while the new content is
+        // loading.
         $pane.html('<div class="spinner spinner-32px spinner-h-center"></div>');
         return;
     }
diff --git a/apps/workbench/app/views/jobs/_show_status.html.erb b/apps/workbench/app/views/jobs/_show_status.html.erb
index 5bb37c3..cfff0da 100644
--- a/apps/workbench/app/views/jobs/_show_status.html.erb
+++ b/apps/workbench/app/views/jobs/_show_status.html.erb
@@ -7,7 +7,7 @@
      data-object-uuid="<%= @object.uuid %>"
      data-load-throttle="5000"
      >
-  <div id="job-status-pane" class="active">
+  <div id="job-status-pane">
     <%=
        pj = {}
        pj[:job] = @object
diff --git a/apps/workbench/app/views/jobs/show.html.erb b/apps/workbench/app/views/jobs/show.html.erb
index d83bc72..566014e 100644
--- a/apps/workbench/app/views/jobs/show.html.erb
+++ b/apps/workbench/app/views/jobs/show.html.erb
@@ -1,5 +1,5 @@
 <% content_for :tab_line_buttons do %>
-  <div class="active pane-loaded arv-log-event-listener arv-refresh-on-state-change"
+  <div class="pane-loaded arv-log-event-listener arv-refresh-on-state-change"
        data-pane-content-url="<%= url_for(params.merge(tab_pane: "job_buttons")) %>"
        data-object-uuid="<%= @object.uuid %>"
        style="display: inline">
diff --git a/apps/workbench/app/views/pipeline_instances/_show_log.html.erb b/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
index 2a71aef..060a669 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
@@ -1,7 +1,7 @@
 <% log_uuids = [@object.uuid] + pipeline_jobs(@object).collect{|x|x[:job].andand[:uuid]}.compact %>
 <% log_history = stderr_log_history(log_uuids) %>
 <div id="pipeline_event_log_div"
-     class="active arv-log-event-listener arv-log-event-handler-append-logs arv-log-event-subscribe-to-pipeline-job-uuids arv-job-log-window"
+     class="arv-log-event-listener arv-log-event-handler-append-logs arv-log-event-subscribe-to-pipeline-job-uuids arv-job-log-window"
      data-object-uuids="<%= log_uuids.join(' ') %>"
      ><%= log_history.join("\n") %></div>
 <div class="arv-log-refresh-control"

commit 24d3d682fc97e24ab06e1335953b140b2e49547f
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Nov 4 22:40:48 2014 -0500

    4084: Fix up whitespace (4-space indent, like the other *.js)

diff --git a/apps/workbench/app/assets/javascripts/event_log.js b/apps/workbench/app/assets/javascripts/event_log.js
index 8df099a..36361a1 100644
--- a/apps/workbench/app/assets/javascripts/event_log.js
+++ b/apps/workbench/app/assets/javascripts/event_log.js
@@ -4,55 +4,55 @@
 
 /* Subscribe to websockets event log.  Do nothing if already connected. */
 function subscribeToEventLog () {
-  // if websockets are not supported by browser, do not subscribe for events
-  websocketsSupported = ('WebSocket' in window);
-  if (websocketsSupported == false) {
-    return;
-  }
-
-  // check if websocket connection is already stored on the window
-  event_log_disp = $(window).data("arv-websocket");
-  if (event_log_disp == null) {
-    // need to create new websocket and event log dispatcher
-    websocket_url = $('meta[name=arv-websocket-url]').attr("content");
-    if (websocket_url == null)
-      return;
-
-    event_log_disp = new WebSocket(websocket_url);
-
-    event_log_disp.onopen = onEventLogDispatcherOpen;
-    event_log_disp.onmessage = onEventLogDispatcherMessage;
-
-    // store websocket in window to allow reuse when multiple divs subscribe for events
-    $(window).data("arv-websocket", event_log_disp);
-  }
+    // if websockets are not supported by browser, do not subscribe for events
+    websocketsSupported = ('WebSocket' in window);
+    if (websocketsSupported == false) {
+        return;
+    }
+
+    // check if websocket connection is already stored on the window
+    event_log_disp = $(window).data("arv-websocket");
+    if (event_log_disp == null) {
+        // need to create new websocket and event log dispatcher
+        websocket_url = $('meta[name=arv-websocket-url]').attr("content");
+        if (websocket_url == null)
+            return;
+
+        event_log_disp = new WebSocket(websocket_url);
+
+        event_log_disp.onopen = onEventLogDispatcherOpen;
+        event_log_disp.onmessage = onEventLogDispatcherMessage;
+
+        // store websocket in window to allow reuse when multiple divs subscribe for events
+        $(window).data("arv-websocket", event_log_disp);
+    }
 }
 
 /* Send subscribe message to the websockets server.  Without any filters
    arguments, this subscribes to all events */
 function onEventLogDispatcherOpen(event) {
-  this.send('{"method":"subscribe"}');
+    this.send('{"method":"subscribe"}');
 }
 
 /* Trigger event for all applicable elements waiting for this event */
 function onEventLogDispatcherMessage(event) {
-  parsedData = JSON.parse(event.data);
-  object_uuid = parsedData.object_uuid;
+    parsedData = JSON.parse(event.data);
+    object_uuid = parsedData.object_uuid;
 
-  if (!object_uuid) {
-    return;
-  }
+    if (!object_uuid) {
+        return;
+    }
 
-  // if there are any listeners for this object uuid or "all", trigger the event
-  matches = ".arv-log-event-listener[data-object-uuid=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuids~=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuid=\"all\"],.arv-log-event-listener[data-object-kind=\"" + parsedData.object_kind + "\"]";
-  $(matches).trigger('arv-log-event', parsedData);
+    // if there are any listeners for this object uuid or "all", trigger the event
+    matches = ".arv-log-event-listener[data-object-uuid=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuids~=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuid=\"all\"],.arv-log-event-listener[data-object-kind=\"" + parsedData.object_kind + "\"]";
+    $(matches).trigger('arv-log-event', parsedData);
 }
 
 /* Automatically connect if there are any elements on the page that want to
    receive event log events. */
 $(document).on('ajax:complete ready', function() {
-  var a = $('.arv-log-event-listener');
-  if (a.length > 0) {
-    subscribeToEventLog();
-  }
+    var a = $('.arv-log-event-listener');
+    if (a.length > 0) {
+        subscribeToEventLog();
+    }
 });

commit 5257d99306e915cc9bb5a65eca81f8517e6e222d
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Nov 4 19:25:08 2014 -0500

    4084: Move all pane state into $pane, flatten panes with multiple entry points.

diff --git a/apps/workbench/app/assets/javascripts/event_log.js b/apps/workbench/app/assets/javascripts/event_log.js
index 9e03dff..8df099a 100644
--- a/apps/workbench/app/assets/javascripts/event_log.js
+++ b/apps/workbench/app/assets/javascripts/event_log.js
@@ -36,18 +36,20 @@ function onEventLogDispatcherOpen(event) {
 
 /* Trigger event for all applicable elements waiting for this event */
 function onEventLogDispatcherMessage(event) {
-    parsedData = JSON.parse(event.data);
-    object_uuid = parsedData.object_uuid;
-
-    if (object_uuid) {
-        // if there are any listeners for this object uuid or "all", trigger the event
-        matches = ".arv-log-event-listener[data-object-uuid=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuids~=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuid=\"all\"],.arv-log-event-listener[data-object-kind=\"" + parsedData.object_kind + "\"]";
-        $(matches).trigger('arv-log-event', parsedData);
-    }
+  parsedData = JSON.parse(event.data);
+  object_uuid = parsedData.object_uuid;
+
+  if (!object_uuid) {
+    return;
+  }
+
+  // if there are any listeners for this object uuid or "all", trigger the event
+  matches = ".arv-log-event-listener[data-object-uuid=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuids~=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuid=\"all\"],.arv-log-event-listener[data-object-kind=\"" + parsedData.object_kind + "\"]";
+  $(matches).trigger('arv-log-event', parsedData);
 }
 
 /* Automatically connect if there are any elements on the page that want to
-   received event log events. */
+   receive event log events. */
 $(document).on('ajax:complete ready', function() {
   var a = $('.arv-log-event-listener');
   if (a.length > 0) {
diff --git a/apps/workbench/app/assets/javascripts/pipeline_instances.js b/apps/workbench/app/assets/javascripts/pipeline_instances.js
index 78fd195..15134cb 100644
--- a/apps/workbench/app/assets/javascripts/pipeline_instances.js
+++ b/apps/workbench/app/assets/javascripts/pipeline_instances.js
@@ -47,7 +47,7 @@ $(document).on('ready ajax:complete', function() {
     run_pipeline_button_state();
 });
 
-$(document).on('arv-log-event', '.arv-refresh-on-state-change', function(event, eventData){
+$(document).on('arv-log-event', '.arv-refresh-on-state-change', function(event, eventData) {
     if (eventData.event_type == "update" &&
         eventData.properties.old_attributes.state != eventData.properties.new_attributes.state)
     {
@@ -56,18 +56,30 @@ $(document).on('arv-log-event', '.arv-refresh-on-state-change', function(event,
 });
 
 $(document).on('arv-log-event', '.arv-log-event-subscribe-to-pipeline-job-uuids', function(event, eventData){
-    if (eventData.event_type == "create" || eventData.event_type == "update") {
-        if (eventData.object_kind == 'arvados#pipelineInstance') {
-            var objs = "";
-            var components = eventData.properties.new_attributes.components;
-            for (a in components) {
-                if (components[a].job && components[a].job.uuid) {
-                    objs += " " + components[a].job.uuid;
-                }
-            }
-            $(event.target).attr("data-object-uuids", eventData.object_uuid + objs);
+    if (!((eventData.object_kind == 'arvados#pipelineInstance') &&
+          (eventData.event_type == "create" ||
+           eventData.event_type == "update") &&
+         eventData.properties &&
+         eventData.properties.new_attributes &&
+         eventData.properties.new_attributes.components)) {
+        return;
+    }
+    var objs = "";
+    var components = eventData.properties.new_attributes.components;
+    for (a in components) {
+        if (components[a].job && components[a].job.uuid) {
+            objs += " " + components[a].job.uuid;
         }
     }
+    $(event.target).attr("data-object-uuids", eventData.object_uuid + objs);
+});
+
+$(document).on('ready ajax:success', function() {
+    $('.arv-log-refresh-control').each(function() {
+        var uuids = $(this).attr('data-object-uuids');
+        var $pane = $(this).closest('[data-pane-content-url]');
+        $pane.attr('data-object-uuids', uuids);
+    });
 });
 
 $(document).on('arv-log-event', '.arv-log-event-handler-append-logs', function(event, eventData){
diff --git a/apps/workbench/app/assets/javascripts/tab_panes.js b/apps/workbench/app/assets/javascripts/tab_panes.js
index 4130488..3f1645b 100644
--- a/apps/workbench/app/assets/javascripts/tab_panes.js
+++ b/apps/workbench/app/assets/javascripts/tab_panes.js
@@ -2,26 +2,10 @@
 
 // Fire when a tab is selected/clicked.
 $(document).on('shown.bs.tab', '[data-toggle="tab"]', function(event) {
-    // When we switch tabs, remove "active" from any refreshable panes within
-    // the previous tab content so they don't continue to refresh unnecessarily, and
-    // add "active" to any refreshable panes under the newly shown tab content.
-
-    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");
-    });
-
-    if (!$(event.target).hasClass("pane-loaded")) {
-        // pane needs to be loaded
-        $(event.target).trigger('arv:pane:reload');
-    }
+    // reload the pane (unless it's already loaded)
+    $($(event.target).attr('href')).
+        not('.pane-loaded').
+        trigger('arv:pane:reload');
 });
 
 // Ask a refreshable pane to reload via ajax.
@@ -51,128 +35,142 @@ $(document).on('shown.bs.tab', '[data-toggle="tab"]', function(event) {
 // 5. "pane-loaded pane-reload-pending" indicates a reload is scheduled (but has
 // not started yet), suppressing scheduling of any further reloads.
 //
-$(document).on('arv:pane:reload', function(e) {
-    e.stopPropagation();
+$(document).on('arv:pane:reload', '[data-pane-content-url]', function(e) {
+    // $pane, the event target, is an element whose content is to be
+    // replaced. Pseudoclasses on $pane (pane-loading, etc) encode the
+    // current loading state.
+    var $pane = $(e.target);
+
+    var content_url = $pane.attr('data-pane-content-url');
+    if (!content_url) {
+        // When reloadable elements are nested, we can receive
+        // arv:pane:reload events even though the selector in .on()
+        // does not match e.target. Ignore such events.
+        return;
+    }
 
-    // '$anchor' is the event target, which is a .pane-anchor or a bootstrap
-    // tab anchor.  This is the element that stores the state of the pane.  The
-    // actual element that will contain the content is pointed to in the 'href'
-    // attribute of etarget.
-    var $anchor = $(e.target);
+    e.stopPropagation();
 
-    if ($anchor.hasClass('pane-loading')) {
+    if ($pane.hasClass('pane-loading')) {
         // Already loading, mark stale to schedule a reload after this one.
-        $anchor.addClass('pane-stale');
+        $pane.addClass('pane-stale');
         return;
     }
 
-    var throttle = $anchor.attr('data-load-throttle');
-    if (!throttle) {
-        throttle = 15000;
-    }
+    // The default throttle (mininum milliseconds between refreshes)
+    // can be overridden by an .arv-log-refresh-control element inside
+    // the pane -- or, failing that, the pane element itself -- with a
+    // data-load-throttle attribute. This allows the server to adjust
+    // the throttle depending on the pane content.
+    var throttle =
+        $pane.find('.arv-log-refresh-control').attr('data-load-throttle') ||
+        $pane.attr('data-load-throttle') ||
+        15000;
     var now = (new Date()).getTime();
-    var loaded_at = $anchor.attr('data-loaded-at');
+    var loaded_at = $pane.attr('data-loaded-at');
     var since_last_load = now - loaded_at;
     if (loaded_at && (since_last_load < throttle)) {
-        if (!$anchor.hasClass('pane-reload-pending')) {
-            $anchor.addClass('pane-reload-pending');
+        if (!$pane.hasClass('pane-reload-pending')) {
+            $pane.addClass('pane-reload-pending');
             setTimeout((function() {
-                $anchor.trigger('arv:pane:reload');
+                $pane.trigger('arv:pane:reload');
             }), throttle - since_last_load);
         }
         return;
     }
 
     // We know this doesn't have 'pane-loading' because we tested for it above
-    $anchor.removeClass('pane-reload-pending');
-    $anchor.removeClass('pane-loaded');
-    $anchor.removeClass('pane-stale');
+    $pane.removeClass('pane-reload-pending');
+    $pane.removeClass('pane-loaded');
+    $pane.removeClass('pane-stale');
 
-    // $pane is the actual content area that is going to be updated.
-    var $pane = $($anchor.attr('href'));
-    if ($pane.hasClass('active')) {
-        $anchor.addClass('pane-loading');
+    if (!$pane.hasClass('active')) {
+        // When the user selects e.target tab, show a spinner instead of
+        // old content while loading.
+        $pane.html('<div class="spinner spinner-32px spinner-h-center"></div>');
+        return;
+    }
 
-        var content_url = $anchor.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", this).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');
-                    }
-                });
-                this.html(tmp);
-                $anchor.removeClass('pane-loading');
-                $anchor.addClass('pane-loaded');
-                $anchor.attr('data-loaded-at', (new Date()).getTime());
-                this.trigger('arv:pane:loaded');
+    $pane.addClass('pane-loading');
 
-                if ($anchor.hasClass('pane-stale')) {
-                    $anchor.trigger('arv:pane:reload');
+    $.ajax(content_url, {dataType: 'html', type: 'GET', context: $pane}).
+        done(function(data, status, jqxhr) {
+            // Preserve collapsed state
+            var $pane = this;
+            var collapsable = {};
+            $(".collapse", this).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');
                 }
-            }).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;
-                    }
+            });
+            $pane.html(tmp);
+            $pane.removeClass('pane-loading');
+            $pane.addClass('pane-loaded');
+            $pane.attr('data-loaded-at', (new Date()).getTime());
+            $pane.trigger('arv:pane:loaded');
+
+            if ($pane.hasClass('pane-stale')) {
+                $pane.trigger('arv:pane:reload');
+            }
+        }).fail(function(jqxhr, status, error) {
+            var $pane = this;
+            var errhtml;
+            var contentType = jqxhr.getResponseHeader('Content-Type');
+            if (contentType && contentType.match(/\btext\/html\b/)) {
+                var $response = $(jqxhr.responseText);
+                var $wrapper = $('div#page-wrapper', $response);
+                if ($wrapper.length) {
+                    errhtml = $wrapper.html();
                 } else {
-                    errhtml = ("An error occurred: " +
-                               (jqxhr.responseText || status)).
-                        replace(/&/g, '&').
-                        replace(/</g, '<').
-                        replace(/>/g, '>');
+                    errhtml = jqxhr.responseText;
                 }
-                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 style="width: 100%"></iframe></div>');
-                $('.tab_reload', this).click(function() {
-                    this.html('<div class="spinner spinner-32px spinner-h-center"></div>');
-                    $anchor.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', this)[0];
-                iframe.contentWindow.setTimeout(function() {
-                    $('body', iframe.contentDocument).html(errhtml);
-                    iframe.height = iframe.contentDocument.body.scrollHeight + "px";
-                }, 1);
-                $anchor.removeClass('pane-loading');
-                $anchor.addClass('pane-loaded');
+            } else {
+                errhtml = ("An error occurred: " +
+                           (jqxhr.responseText || status)).
+                    replace(/&/g, '&').
+                    replace(/</g, '<').
+                    replace(/>/g, '>');
+            }
+            $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 style="width: 100%"></iframe></div>');
+            $('.tab_reload', $pane).click(function() {
+                $(this).
+                    html('<div class="spinner spinner-32px spinner-h-center"></div>').
+                    closest('.pane-loaded').
+                    attr('data-loaded-at', 0).
+                    trigger('arv:pane:reload');
             });
-    } else {
-        // When the user selects e.target tab, show a spinner instead of
-        // old content while loading.
-        $pane.html('<div class="spinner spinner-32px spinner-h-center"></div>');
-    }
+            // 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', $pane)[0];
+            iframe.contentWindow.setTimeout(function() {
+                $('body', iframe.contentDocument).html(errhtml);
+                iframe.height = iframe.contentDocument.body.scrollHeight + "px";
+            }, 1);
+            $pane.removeClass('pane-loading');
+            $pane.addClass('pane-loaded');
+        });
 });
 
 // Mark all panes as stale/dirty. Refresh any 'active' panes.
 $(document).on('arv:pane:reload:all', function() {
-    $('.pane-anchor').trigger('arv:pane:reload');
+    $('[data-pane-content-url]').trigger('arv:pane:reload');
 });
 
-$(document).on('ready ajax:complete', function() {
+$(document).on('arv-log-event', '.arv-refresh-on-log-event', function(e) {
     // 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');
-    });
+    $(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
diff --git a/apps/workbench/app/assets/stylesheets/application.css.scss b/apps/workbench/app/assets/stylesheets/application.css.scss
index fc7e462..f1de7d0 100644
--- a/apps/workbench/app/assets/stylesheets/application.css.scss
+++ b/apps/workbench/app/assets/stylesheets/application.css.scss
@@ -267,3 +267,7 @@ span.editable-textile {
 .compute-summary-numbers td {
   font-size: 150%;
 }
+
+.arv-log-refresh-control {
+  display: none;
+}
diff --git a/apps/workbench/app/controllers/pipeline_instances_controller.rb b/apps/workbench/app/controllers/pipeline_instances_controller.rb
index 46fc3d7..3d34c7b 100644
--- a/apps/workbench/app/controllers/pipeline_instances_controller.rb
+++ b/apps/workbench/app/controllers/pipeline_instances_controller.rb
@@ -241,11 +241,9 @@ class PipelineInstancesController < ApplicationController
   end
 
   def show_pane_list
-    panes = %w(Components Graph Advanced)
+    panes = %w(Components Log Graph Advanced)
     if @object and @object.state.in? ['New', 'Ready']
-      panes = %w(Inputs) + panes
-    else
-      panes.insert(1, {:name => "Log"})
+      panes = %w(Inputs) + panes - %w(Log)
     end
     if not @object.components.values.any? { |x| x[:job] rescue false }
       panes -= ['Graph']
diff --git a/apps/workbench/app/views/application/_content.html.erb b/apps/workbench/app/views/application/_content.html.erb
index d69e358..7afc824 100644
--- a/apps/workbench/app/views/application/_content.html.erb
+++ b/apps/workbench/app/views/application/_content.html.erb
@@ -7,12 +7,11 @@
       <% pane_name = (pane.is_a?(Hash) ? pane[:name] : pane) %>
       <li class="<%= 'active' if i==0 %>">
         <a href="#<%= pane_name %>"
-           class="pane-anchor"
            id="<%= pane_name %>-tab"
            data-toggle="tab"
            data-tab-history=true
            data-tab-history-update-url=true
-           data-pane-content-url="<%= url_for(params.merge(tab_pane: pane_name)) %>">
+           >
           <%= pane_name.gsub('_', ' ') %> <span id="<%= pane_name %>-count"></span>
         </a>
       </li>
@@ -23,21 +22,22 @@
     <% pane_list.each_with_index do |pane, i| %>
       <% pane_name = (pane.is_a?(Hash) ? pane[:name] : pane) %>
       <div id="<%= pane_name %>"
-           class="tab-pane <%= 'in active pane-loaded' if i==0 %>"
+           class="tab-pane fade <%= 'in active loaded pane-loaded' if i==0 %> arv-log-event-listener arv-refresh-on-log-event arv-log-event-subscribe-to-pipeline-job-uuids"
            <% if controller.action_name == "index" %>
              data-object-kind="arvados#<%= ArvadosApiClient.class_kind controller.model_class %>"
            <% else %>
              data-object-uuid="<%= @object.uuid %>"
            <% end %>
+           data-pane-content-url="<%= url_for(params.merge(tab_pane: pane_name)) %>"
            style="margin-top:0.5em;"
-      >
-          <div class="pane-content">
-            <% if i == 0 %>
-              <%= render_pane pane_name, to_string: true %>
-            <% else %>
-              <div class="spinner spinner-32px spinner-h-center"></div>
-            <% end %>
-          </div>
+           >
+        <div class="pane-content">
+          <% if i == 0 %>
+            <%= render_pane pane_name, to_string: true %>
+          <% else %>
+            <div class="spinner spinner-32px spinner-h-center"></div>
+          <% end %>
+        </div>
       </div>
     <% end %>
   </div>
diff --git a/apps/workbench/app/views/jobs/_show_job_component.html.erb b/apps/workbench/app/views/jobs/_show_job_component.html.erb
deleted file mode 100644
index 6ec6c7f..0000000
--- a/apps/workbench/app/views/jobs/_show_job_component.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<% pj = {} %>
-<% pj[:job] = @object %>
-<% pj[:name] = @object[:name] || "this job" %>
-<% pj[:progress_bar] = render(partial: "job_progress", locals: {:j => @object }) %>
-<% tasks = JobTask.filter([['job_uuid', '=', @object.uuid]]).results %>
-<%= render partial: 'pipeline_instances/running_component', locals: {tasks: tasks, pj: pj, i: 0, expanded: true} %>
diff --git a/apps/workbench/app/views/jobs/_show_status.html.erb b/apps/workbench/app/views/jobs/_show_status.html.erb
index 976bf19..5bb37c3 100644
--- a/apps/workbench/app/views/jobs/_show_status.html.erb
+++ b/apps/workbench/app/views/jobs/_show_status.html.erb
@@ -8,7 +8,16 @@
      data-load-throttle="5000"
      >
   <div id="job-status-pane" class="active">
-    <%= render 'show_job_component' %>
+    <%=
+       pj = {}
+       pj[:job] = @object
+       pj[:name] = @object[:name] || "this job"
+       pj[:progress_bar] = render(partial: "job_progress",
+                                  locals: {:j => @object })
+       tasks = JobTask.filter([['job_uuid', '=', @object.uuid]]).results
+       render(partial: 'pipeline_instances/running_component',
+              locals: { tasks: tasks, pj: pj, i: 0, expanded: true})
+    %>
   </div>
 </div>
 </div>
diff --git a/apps/workbench/app/views/jobs/show.html.erb b/apps/workbench/app/views/jobs/show.html.erb
index 5f46505..d83bc72 100644
--- a/apps/workbench/app/views/jobs/show.html.erb
+++ b/apps/workbench/app/views/jobs/show.html.erb
@@ -1,14 +1,9 @@
 <% content_for :tab_line_buttons do %>
-  <div id="job-tab-buttons"
-       class="pane-anchor active arv-log-event-listener arv-refresh-on-state-change"
-       href="#job-tab-buttons-pane"
+  <div class="active pane-loaded arv-log-event-listener arv-refresh-on-state-change"
        data-pane-content-url="<%= url_for(params.merge(tab_pane: "job_buttons")) %>"
        data-object-uuid="<%= @object.uuid %>"
-       style="display: inline"
-       >
-    <div id="job-tab-buttons-pane" class="active" style="display: inline">
-      <%= render partial: 'show_job_buttons', locals: {object: @object}%>
-    </div>
+       style="display: inline">
+    <%= render partial: 'show_job_buttons', locals: {object: @object}%>
   </div>
 <% end %>
 
diff --git a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
index f77379a..82f18e7 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
@@ -1,21 +1,16 @@
 <% if !@object.state.in? ['New', 'Ready'] %>
 
-<% job_uuids = @object.components.map { |k,j| j.is_a? Hash and j[:job].andand[:uuid] }.compact %>
-
-<div id="pipeline-instance-components"
-     class="pane-anchor arv-log-event-listener arv-refresh-on-log-event"
-     href="#pipeline-instance-components-pane"
-     data-pane-content-url="<%= url_for(params.merge(tab_pane: "components_running")) %>"
-     data-object-uuids="<%= @object.uuid %> <%= job_uuids.join(' ') %>"
-     data-load-throttle="5000"
-     >
-  <div id="pipeline-instance-components-pane" class="active">
+  <%
+     job_uuids = @object.components.map { |k,j| j.is_a? Hash and j[:job].andand[:uuid] }.compact
+     throttle = @object.state == 'Running' ? 5000 : 15000
+     %>
+  <div class="arv-log-refresh-control"
+       data-load-throttle="<%= throttle %>"
+       data-object-uuids="<%= @object.uuid %> <%= job_uuids.join(' ') %>"
+       ></div>
 
   <%= render_pipeline_components("running", :json) %>
 
-</div>
-</div>
-
 <% else %>
   <%# state is either New or Ready %>
   <p><i>Here are all of the pipeline's components (jobs that will need to run in order to complete the pipeline). If you know what you're doing (or you're experimenting) you can modify these parameters before starting the pipeline. Usually, you only need to edit the settings presented on the "Inputs" tab above.</i></p>
diff --git a/apps/workbench/app/views/pipeline_instances/_show_log.html.erb b/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
index 5bc7828..2a71aef 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_log.html.erb
@@ -1,4 +1,9 @@
 <% log_uuids = [@object.uuid] + pipeline_jobs(@object).collect{|x|x[:job].andand[:uuid]}.compact %>
 <% log_history = stderr_log_history(log_uuids) %>
-<div class="arv-log-event-listener arv-log-event-handler-append-logs arv-log-event-subscribe-to-pipeline-job-uuids arv-job-log-window" id="pipeline_event_log_div" data-object-uuids="<%= log_uuids.join(' ') %>"><%= log_history.join("\n") %>
-</div>
+<div id="pipeline_event_log_div"
+     class="active arv-log-event-listener arv-log-event-handler-append-logs arv-log-event-subscribe-to-pipeline-job-uuids arv-job-log-window"
+     data-object-uuids="<%= log_uuids.join(' ') %>"
+     ><%= log_history.join("\n") %></div>
+<div class="arv-log-refresh-control"
+     data-load-throttle="86486400000" <%# 1001 nights %>
+     ></div>

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list