Krinkle has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/57899


Change subject: Zuul status page: Rewrite (bug fixes, bootstrap markup, 
redesign)
......................................................................

Zuul status page: Rewrite (bug fixes, bootstrap markup, redesign)

Bug fixes:
* Fix race condition where JS will start another update
  while the previous one has not ended yet (e.g. timeout or
  slow server) by exposing the Promise object from $.ajax
  and scheduling the next update asynchronously from the complete
  callback instead of directly after calling update();
* Added an error handler for the ajax call. Previously an error
  would be silently ignored and old data removed. Now the old
  data stays on the page and an error message is added on top.

Enhancements:
* Add sample JSON files for local testing (triggered by
  adding ?demo=basic or ?demo=openstack).
* Only re-parse the DOM if there was a change.

Cleanup:
* Prefix class names with 'zuul-'.
* Remove unused css code (presumably copied from openstack).
* Simplified logic result class determination.

Redesign:
* The page is dimmed until the first update is ready. This makes
  for a smoother experience by not having the page redraw a couple
  times but instead present it as a whole.
* The "message" contaitainer now slides in/out when it activates
  or deactivates.
* Use Bootstrap markup and classes for the overal design.
  Added a few extra classes to better fit the content (not all
  scenarios are covered best with the default bootstrap css).
* Using a nav-list construction for the job lists. This means
  the links occupy the list as a block-level element, making the
  entire row clickable instead of just the text at the beginning
  of the line.
* Use lowercase labels instead of uppercase for the job result.
* Enable the footer of the integration-wide template class.
* Move WMF logo to the left and center it vertically with the
  page heading. Also swapped it for the version without the
  "Foundation" subtitle.
* Change (ellipsis-ified) queue title from <a> to <abbr>.
  Having an anchor tag with a title looks weird as it has no
  href, <abbr> seems like the better fit here.

Change-Id: Ifa93f856f29e1a2fe7b27e0b5a9d7e43935d8acc
---
M org/wikimedia/integration/zuul/index.php
A org/wikimedia/integration/zuul/sample-status-basic.json
A org/wikimedia/integration/zuul/sample-status-openstack.json
M org/wikimedia/integration/zuul/status.js
M shared/IntegrationPage.php
5 files changed, 700 insertions(+), 154 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/integration/docroot 
refs/changes/99/57899/1

diff --git a/org/wikimedia/integration/zuul/index.php 
b/org/wikimedia/integration/zuul/index.php
index 0987bae..953bab1 100644
--- a/org/wikimedia/integration/zuul/index.php
+++ b/org/wikimedia/integration/zuul/index.php
@@ -1,7 +1,7 @@
 <?php
 require_once( __DIR__ . '/../../../../shared/IntegrationPage.php' );
 
-$p = IntegrationPage::newFromPageName( 'Zuul status page' );
+$p = IntegrationPage::newFromPageName( 'Zuul Status' );
 $p->setDir( __DIR__ );
 $p->setRootDir( dirname( __DIR__ ) );
 
@@ -9,93 +9,103 @@
 /**
  * Zuul status
  */
-.change {
-       border: 1px solid #95c7db;
-       margin-top: 10px;
-       padding: 2px;
+.zuul-container {
+       transition-property: opacity, background-color;
+       transition-duration: 1s;
+       transition-timing-function: ease-in-out;
+       clear: both;
+       opacity: 0;
+       cursor: progress;
+       min-height: 200px;
+       background-color: #f8ffaa;
 }
 
-.change > .header {
-       background: #E2ECEF;
-       color: black;
-       margin: -2px -2px 2px -2px;
-       padding: 4px;
+.zuul-container-ready {
+       opacity: 1;
+       cursor: auto;
+       background-color: #fff;
 }
 
-.change > .header > .changeid {
-       float: right;
+.zuul-spinner {
+       opacity: 0;
+       transition: opacity 6s ease-in-out;
+       cursor: default;
+}
+.zuul-spinner-on {
+       opacity: 1;
+       transition-duration: 0.5s;
+       cursor: progress;
 }
 
-.job {
-       display: block;
-}
-
-.pipeline {
-       float: left;
-       width: 25em;
-       padding: 4px;
-}
-
-.pipeline > .header {
-       background: #0000cc;
-       color: white;
-}
-
-.arrow {
+.zuul-change-arrow {
        text-align: center;
        font-size: 16pt;
        line-height: 1.0;
 }
 
-.result_success {
-       color: #007f00;
+.zuul-change-id {
+       text-transform: none;
+       float: right;
 }
 
-.result_failure {
-       color: #cf2f19;
+.zuul-change-job a {
+       overflow: auto;
 }
 
-.result_unstable {
-       color: #e39f00;
+.zuul-result {
+       text-shadow: none;
+       font-weight: normal;
+       float: right;
+       background-color: #E9E9E9;
+       color: #555;
 }
 
-a:link {
-       color: #204A87;
+.zuul-result.label-success {
+       background-color: #CDF0CD;
+       color: #468847;
 }
 
-#message p {
+.zuul-result.label-important {
+       background-color: #F1DBDA;
+       color: #B94A48;
+}
+
+.zuul-result.label-warning {
+       background-color: #F3E6D4;
+       color: #F89406;
+}
+
+.zuul-msg-wrap {
+       max-height: 150px;
+       overflow: hidden;
+}
+
+.zuul-container-ready .zuul-msg-wrap {
+       transition: max-height 1s ease-in;
+}
+
+.zuul-msg-wrap-off {
+       max-height: 0;
+}
+
+.zuul-msg p {
        margin: 0;
-}
-
-.alertbox {
-       border: 1px solid #e5574d;
-       background: #ffaba5;
-       color: black;
-       padding: 1em;
-       font-size: 12pt;
-       margin: 0pt;
 }
 ');
 
 $p->addHtmlContent('
-<p>This is the status page for the Zuul daemon on Wikimedia infrastructure.</p>
+<p>Real-time status monitor of Zuul, the pipeline manager between Gerrit and 
Jenkins. <a 
href="https://www.mediawiki.org/wiki/Continuous_integration/Zuul";>more info 
&raquo;</a></p>
 
-<p>Queue lengths: <span id="trigger_event_queue_length"></span> events,
-<span id="result_event_queue_length"></span> results.</p>
-
-<div class="container">
-       <div id="message"></div>
-</div>
-
-<div id="pipeline-container"></div>
-
-<!-- TODO: add graphite
-<div class="container" id="graph-container">
+<div class="zuul-container" id="zuul-container">
+       <p>Queue lengths: <span id="zuul-eventqueue-length">..</span> events, 
<span id="zuul-resulteventqueue-length">..</span> results.</p>
+       <div id="zuul-pipelines" class="row"></div>
+       <!-- TODO: add graphite
        <h2>Job statistics</h2>
+       -->
 </div>
--->
 ');
 
+$p->enableFooter();
 $p->addScript( 'status.js' );
 
 $p->flush();
diff --git a/org/wikimedia/integration/zuul/sample-status-basic.json 
b/org/wikimedia/integration/zuul/sample-status-basic.json
new file mode 100644
index 0000000..0994eac
--- /dev/null
+++ b/org/wikimedia/integration/zuul/sample-status-basic.json
@@ -0,0 +1,164 @@
+{
+    "message": "Example error message",
+    "pipelines": [
+        {
+            "name": "test",
+            "description": "Lint and unit tests",
+            "change_queues": [
+                {
+                    "name": "some-j...@worker301.ci-example.org",
+                    "heads": [
+                        [
+                            {
+                                "id": "10101,1",
+                                "url": "#!/review.example.org/r/10101",
+                                "project": "openstack/infra/zuul",
+                                "jobs": [
+                                    {
+                                        "name": "zuul-merge",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-merge/201",
+                                        "result": "SUCCESS",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-lint",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-lint/201",
+                                        "result": "SUCCESS",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-test",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-test/201",
+                                        "result": "SUCCESS",
+                                        "voting": true
+                                    }
+                                ]
+                            }
+                        ],
+                        [
+                            {
+                                "id": "10103,1",
+                                "url": "#!/review.example.org/r/10103",
+                                "project": "google/gerrit",
+                                "jobs": [
+                                    {
+                                        "name": "gerrit-merge",
+                                        "url": 
"#!/jenkins.example.org/job/gerrit-merge/203",
+                                        "result": "SUCCESS",
+                                        "voting": false
+                                    }
+                                ]
+                            }
+                        ]
+                    ]
+                },
+                {
+                    "name": "other-j...@worker301.ci-example.org",
+                    "heads": [
+                        [
+                            {
+                                "id": "10102,1",
+                                "url": "#!/review.example.org/r/10102",
+                                "project": "google/gerrit",
+                                "jobs": [
+                                    {
+                                        "name": "gerrit-merge",
+                                        "url": 
"#!/jenkins.example.org/job/gerrit-merge/202",
+                                        "result": "UNSTABLE",
+                                        "voting": false
+                                    }
+                                ]
+                            }
+                        ],
+                        [
+                            {
+                                "id": "10104,1",
+                                "url": "#!/review.example.org/r/10104",
+                                "project": "openstack/infra/zuul",
+                                "jobs": [
+                                    {
+                                        "name": "zuul-merge",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-merge/204",
+                                        "result": "SUCCESS",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-lint",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-lint/204",
+                                        "result": "FAILURE",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-test",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-test/204",
+                                        "result": null,
+                                        "voting": true
+                                    }
+                                ]
+                            }
+                        ]
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "gate-and-submit",
+            "change_queues": []
+        },
+        {
+            "name": "postmerge",
+            "change_queues": [
+                {
+                    "name": "some-j...@worker301.ci-example.org",
+                    "heads": [
+                        [
+                            {
+                                "id": 
"7f1d65cb0f663c907698f915da01c008c7ef4748",
+                                "url": "#!/review.example.org/r/10100",
+                                "project": "openstack/infra/zuul",
+                                "jobs": [
+                                    {
+                                        "name": "zuul-lint",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-lint/200",
+                                        "result": "SUCCESS",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-test",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-test/200",
+                                        "result": "SUCCESS",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-regression",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-regression/200",
+                                        "result": "FAILURE",
+                                        "voting": true
+                                    },
+                                    {
+                                        "name": "zuul-performance",
+                                        "url": 
"#!/jenkins.example.org/job/zuul-performance/200",
+                                        "result": null,
+                                        "voting": false
+                                    },
+                                    {
+                                        "name": "zuul-docs-publish",
+                                        "url": null,
+                                        "result": null,
+                                        "voting": true
+                                    }
+                                ]
+                            }
+                        ]
+                    ]
+                }
+            ]
+        }
+    ],
+    "trigger_event_queue": {
+        "length": 0
+    },
+    "result_event_queue": {
+        "length": 0
+    }
+}
diff --git a/org/wikimedia/integration/zuul/sample-status-openstack.json 
b/org/wikimedia/integration/zuul/sample-status-openstack.json
new file mode 100644
index 0000000..5d16bdd
--- /dev/null
+++ b/org/wikimedia/integration/zuul/sample-status-openstack.json
@@ -0,0 +1,310 @@
+{
+    "pipelines": [
+        {
+            "name": "check",
+            "description": "Newly uploaded patchsets enter this pipeline to 
receive an initial +/-1 Verified vote from Jenkins.",
+            "change_queues": [
+                {
+                    "heads": [],
+                    "name": "stackforge/tripleo-image-elements"
+                }
+            ]
+        },
+        {
+            "description": "Changes that have been approved by core developers 
are enqueued in order in this pipeline, and .",
+            "change_queues": [
+                {
+                    "heads": [],
+                    "name": "openstack-detackforge/reddwarf-integration"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/moniker"
+                },
+                {
+                    "heads": [
+                        [
+                            {
+                                "url": "https://review.openstack.org/26292";,
+                                "project": "openstack/quantum",
+                                "jobs": [
+                                    {
+                                        "url": 
"https://jenkins.openstack.org/job/gate-quantum-docs/5501/consoleFull";,
+                                        "voting": true,
+                                        "result": "SUCCESS",
+                                        "name": "gate-quantum-docs"
+                                    },
+                                    {
+                                        "url": 
"https://jenkins.openstack.org/job/gate-quantum-pep8/6254/consoleFull";,
+                                        "voting": true,
+                                        "result": "SUCCESS",
+                                        "name": "gate-quantum-pep8"
+                                    },
+                                    {
+                                        "url": 
"https://jenkins.openstack.org/job/gate-quantum-python26/5876/";,
+                                        "voting": true,
+                                        "result": null,
+                                        "name": "gate-quantum-python26"
+                                    },
+                                    {
+                                        "url": 
"https://jenkins.openstack.org/job/gate-quantum-python27/5887/";,
+                                        "voting": true,
+                                        "result": null,
+                                        "name": "gate-quantum-python27"
+                                    },
+                                    {
+                                        "url": 
"https://jenkins.openstack.org/job/gate-tempest-devstack-vm-quantum/17548/";,
+                                        "voting": true,
+                                        "result": null,
+                                        "name": 
"gate-tempest-devstack-vm-quantum"
+                                    }
+                                ],
+                                "id": "26292,1"
+                            }
+                        ]
+                    ],
+                    "name": "openstack-dev/devstack, 
openstack-infra/devstack-gate, openstack/cinder, openstack/glance, 
openstack/horizon, openstack/keystone, openstack/nova, 
openstack/python-cinderclient, openstack/python-glanceclient, 
openstack/python-keystoneclient, openstack/python-novaclient, 
openstack/python-quantumclient, openstack/quantum, openstack/swift, 
openstack/tempest, z/tempest"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/ceilometer"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-openstack"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-cinder"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/marconi"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/config"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/tripleo-image-elements"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/kwapi"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/python-reddwarfclient"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/python-savannaclient"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/python-monikerclient"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/packstack"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/oslo.config"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/jenkins-job-builder"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-horizon"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/heat-cfntools"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/oslo-incubator"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/os-config-applier"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/requirements"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-glance"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/gearman-plugin"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-keystone"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-nova"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/climate"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/python-swiftclient"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/python-ceilometerclient"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/git-review"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/bufunfa"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/puppet-swift"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/statusbot"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/openstack-planet"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/python-openstackclient"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/diskimage-builder"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/gerritlib"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/zuul"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/reddwarf"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-dev/hacking"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/python-heatclient"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/python-libraclient"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/reviewday"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/jeepyb"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/heat"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/libra"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/gerrit"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/healthnmon"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-infra/gerritbot"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack-dev/pbr"
+                },
+                {
+                    "heads": [],
+                    "name": "stackforge/savanna"
+                },
+                {
+                    "heads": [],
+                    "name": "openstack/openstack-manuals"
+                }
+            ],
+            "name": "gate"
+        },
+        {
+            "description": "This pipeline runs jobs that operate after each 
change is merged.",
+            "change_queues": [
+                {
+                    "heads": [],
+                    "name": "openstack-dev/hacking, 
openstack-dev/openstack-qa, openstack-dev/pbr, openstack-infra/config, 
openstack-infra/gearman-plugin, openstack-infra/gerrit, 
openstack-infra/gerritbot, openstack-infra/git-review, 
openstack-infra/jenkins-job-builder, openstack-infra/nose-html-output, 
openstack-infra/reviewday, openstack-infra/statusbot, openstack-infra/zuul, 
openstack/api-site, openstack/ceilometer, openstack/cinder, 
openstack/compute-api, openstack/glance, openstack/heat, 
openstack/heat-cfntools, openstack/horizon, openstack/identity-api, 
openstack/image-api, openstack/keystone, openstack/netconn-api, openstack/nova, 
openstack/object-api, openstack/openstack-manuals, openstack/oslo-incubator, 
openstack/oslo.config, openstack/python-ceilometerclient, 
openstack/python-cinderclient, openstack/python-glanceclient, 
openstack/python-heatclient, openstack/python-keystoneclient, 
openstack/python-novaclient, openstack/python-openstackclient, 
openstack/python-quantumclient, openstack/python-swiftclient, 
openstack/quantum, openstack/requirements, openstack/swift, 
openstack/volume-api, stackforge/bufunfa, stackforge/diskimage-builder, 
stackforge/moniker, stackforge/os-config-applier, 
stackforge/python-monikerclient, stackforge/python-savannaclient, 
stackforge/reddwarf, stackforge/savanna, stackforge/tripleo-image-elements"
+                }
+            ],
+            "name": "post"
+        },
+        {
+            "description": "This pipeline runs jobs on projects in response to 
pre-release tags.",
+            "change_queues": [
+                {
+                    "heads": [],
+                    "name": "openstack-dev/hacking, openstack-dev/pbr, 
openstack-infra/gerritbot, openstack-infra/gerritlib, 
openstack-infra/git-review, openstack-infra/jeepyb, 
openstack-infra/jenkins-job-builder, openstack-infra/nose-html-output, 
openstack-infra/reviewday, openstack-infra/statusbot, openstack-infra/zuul, 
openstack/ceilometer, openstack/cinder, openstack/glance, openstack/heat, 
openstack/heat-cfntools, openstack/horizon, openstack/keystone, openstack/nova, 
openstack/oslo.config, openstack/python-ceilometerclient, 
openstack/python-cinderclient, openstack/python-glanceclient, 
openstack/python-heatclient, openstack/python-keystoneclient, 
openstack/python-novaclient, openstack/python-openstackclient, 
openstack/python-quantumclient, openstack/python-swiftclient, 
openstack/quantum, openstack/swift, stackforge/moniker, 
stackforge/python-monikerclient, stackforge/python-reddwarfclient, 
stackforge/python-savannaclient, stackforge/savanna"
+                }
+            ],
+            "name": "pre-release"
+        },
+        {
+            "description": "When a commit is tagged as a release, this 
pipeline runs jobs that publish archives and documentation.",
+            "change_queues": [
+                {
+                    "heads": [],
+                    "name": "openstack-dev/hacking, 
openstack-dev/openstack-qa, openstack-dev/pbr, openstack-infra/gerritbot, 
openstack-infra/gerritlib, openstack-infra/git-review, openstack-infra/jeepyb, 
openstack-infra/jenkins-job-builder, openstack-infra/nose-html-output, 
openstack-infra/reviewday, openstack-infra/statusbot, openstack-infra/zuul, 
openstack/ceilometer, openstack/cinder, openstack/glance, openstack/heat, 
openstack/heat-cfntools, openstack/horizon, openstack/keystone, openstack/nova, 
openstack/oslo-incubator, openstack/oslo.config, 
openstack/python-ceilometerclient, openstack/python-cinderclient, 
openstack/python-glanceclient, openstack/python-heatclient, 
openstack/python-keystoneclient, openstack/python-novaclient, 
openstack/python-openstackclient, openstack/python-quantumclient, 
openstack/python-swiftclient, openstack/quantum, openstack/swift, 
stackforge/moniker, stackforge/python-monikerclient, 
stackforge/python-reddwarfclient, stackforge/python-savannaclient, 
stackforge/savanna"
+                }
+            ],
+            "name": "release"
+        },
+        {
+            "description": "This pipeline is used for silently testing new 
jobs.",
+            "change_queues": [
+                {
+                    "heads": [],
+                    "name": ""
+                }
+            ],
+            "name": "silent"
+        }
+    ],
+    "trigger_event_queue": {
+        "length": 0
+    },
+    "result_event_queue": {
+        "length": 0
+    }
+}
diff --git a/org/wikimedia/integration/zuul/status.js 
b/org/wikimedia/integration/zuul/status.js
index 79c22a2..5df46af 100644
--- a/org/wikimedia/integration/zuul/status.js
+++ b/org/wikimedia/integration/zuul/status.js
@@ -16,33 +16,42 @@
 // License for the specific language governing permissions and limitations
 // under the License.
 
-/*jshint eqnull:true */
+/*jshint eqnull:true, camelcase:false */
 (function ($) {
-    var updateCount = 0,
-        enableStatusUpdates = true;
+    var $container, $msg, $msgWrap, $indicator, prevHtml,
+        updateCount = 0,
+        enableStatusUpdates = true,
+        demo = location.search.match(/[?&]demo=([^?&]*)/),
+        source = demo ? './sample-status-' + (demo[1] || 'basic') + '.json' : 
'/zuul/status.json';
 
-    function formatPipeline(data) {
-        var html = '<div class="pipeline"><h3 class="subhead">' +
-            data.name + '</h3>';
-        if (data.description != null) {
-            html += '<p>' + data.description + '</p>';
+    /**
+     * @param {Object} pipeline
+     * @return {string} html
+     */
+    function formatPipeline(pipeline) {
+        var html = '<div class="zuul-pipeline span4"><h3>' +
+            pipeline.name + '</h3>';
+        if (pipeline.description != null) {
+            html += '<p><small>' + pipeline.description + '</small></p>';
         }
 
-        $.each(data.change_queues, function (queueNum, changeQueue) {
-            $.each(changeQueue.heads, function (headNum, head) {
-                if (data.change_queues.length > 1 && headNum === 0) {
-                    html += '<div> Change queue: ';
+        $.each(pipeline.change_queues, function (queueNum, changeQueue) {
+            $.each(changeQueue.heads, function (headNum, changes) {
+                if (pipeline.change_queues.length > 1 && headNum === 0) {
+                    html += '<p>Queue: ';
 
                     var name = changeQueue.name;
-                    html += '<a title="' + name + '">';
+                    html += '<abbr title="' + name + '">';
                     if (name.length > 32) {
                         name = name.substr(0, 32) + '...';
                     }
-                    html += name + '</a></div>';
+                    html += name + '</abbr></p>';
                 }
-                $.each(head, function (changeNum, change) {
+                $.each(changes, function (changeNum, change) {
+                    // If there are multiple changes in the same head it means
+                    // they're connected (dependant?)
                     if (changeNum > 0) {
-                        html += '<div class="arrow">&uarr;</div>';
+                        html += '<div class="zuul-change-arrow">&uarr;</div>';
                     }
                     html += formatChange(change);
                 });
@@ -53,16 +62,20 @@
         return html;
     }
 
+    /**
+     * @param {Object} change
+     * @return {string} html
+     */
     function formatChange(change) {
-        var html = '<div class="change"><div class="header">',
+        var html = '<div class="well well-small zuul-change"><ul class="nav 
nav-list">',
             id = change.id,
             url = change.url;
 
-        html += '<span class="project">' + change.project + '</span>';
+        html += '<li class="nav-header">' + change.project;
         if (id.length === 40) {
             id = id.substr(0, 7);
         }
-        html += '<span class="changeid">';
+        html += '<span class="zuul-change-id">';
         if (url != null) {
             html += '<a href="' + url + '">';
         }
@@ -70,98 +83,134 @@
         if (url != null) {
             html += '</a>';
         }
-        html += '</span></div><div class="jobs">';
+        html += '</span></li>';
 
         $.each(change.jobs, function (i, job) {
-            var result = job.result,
-                resultClass = 'result';
-            if (result == null) {
-                if (job.url != null) {
-                    result = 'in progress';
-                } else {
-                    result = 'queued';
-                }
-            } else if (result === 'SUCCESS') {
-                resultClass += ' result_success';
-            } else if (result === 'FAILURE') {
-                resultClass += ' result_failure';
-            } else if (result === 'LOST') {
-                resultClass += ' result_unstable';
-            } else if (result === 'UNSTABLE') {
-                resultClass += ' result_unstable';
+            var result = job.result ? job.result.toLowerCase() : null,
+                resultClass = 'zuul-result label';
+            if (result === null) {
+                result = job.url ? 'in progress' : 'queued';
             }
-            html += '<span class="job">';
+            switch (result) {
+            case 'success':
+                resultClass += ' label-success';
+                break;
+            case 'failure':
+                resultClass += ' label-important';
+                break;
+            case 'lost':
+            case 'unstable':
+                resultClass += ' label-warning';
+                break;
+            }
+            html += '<li class="zuul-change-job">';
             if (job.url != null) {
                 html += '<a href="' + job.url + '">';
             }
             html += job.name;
+            html += ' <span class="' + resultClass + '">' + result + '</span>';
+            if (job.voting === false) {
+                html += ' <span class="muted">(non-voting)</span>';
+            }
             if (job.url != null) {
                 html += '</a>';
             }
-            html += ': <span class="' + resultClass + '">' + result + 
'</span>';
-            if (job.voting === false) {
-                html += ' (non-voting)';
-            }
-            html += '</span>';
+            html += '</li>';
         });
 
-        html += '</div></div>';
+        html += '</ul></div>';
         return html;
     }
 
-    function updateTimeout() {
+    function scheduleUpdate() {
         if (!enableStatusUpdates) {
-            setTimeout(updateTimeout, 5000);
+            setTimeout(scheduleUpdate, 5000);
             return;
         }
 
-        updateCount += 1;
+        update().complete(function () {
 
-        update();
-        /* Only update graphs every minute */
-        if (updateCount > 11) {
-            updateCount = 0;
-            updateGraphs();
-        }
+            updateCount += 1;
 
-        setTimeout(updateTimeout, 5000);
-    }
-
-    function update() {
-
-        $.getJSON('/zuul/status.json', function (data) {
-            data = data || {};
-            var html = '';
-            if ('message' in data) {
-                $('#message')
-                    .attr('class', 'alertbox')
-                    .html(data.message);
-            } else {
-                $('#message')
-                    .removeClass('alertbox')
-                    .empty();
+            // Only update graphs every minute
+            if (updateCount > 12) {
+                updateCount = 1;
+                purgeGraphs();
             }
 
-            html += '<br style="clear:both"/>';
-
-            $.each(data.pipelines, function (i, pipeline) {
-                html = html + formatPipeline(pipeline);
-            });
-
-            html += '<br style="clear:both"/>';
-            $('#pipeline-container').html(html);
-
-            $('#trigger_event_queue_length').text(
-                 (data.trigger_event_queue || []).length
-            );
-            $('#result_event_queue_length').text(
-                 (data.result_event_queue || []).length
-            );
-
+            setTimeout(scheduleUpdate, 5000);
         });
     }
 
-    function updateGraphs() {
+    function updateStart() {
+        $container.addClass('zuul-container-loading');
+        $indicator.addClass('zuul-spinner-on');
+    }
+
+    function updateEnd() {
+        $container.removeClass('zuul-container-loading');
+        setTimeout(function () {
+            $indicator.removeClass('zuul-spinner-on');
+        }, 550);
+        if (updateCount === 0) {
+            // Remove this asynchronous so that if the first
+            // update adds a message, the message does not animate
+            // but appears instantly with the rest of the content.
+            setTimeout(function () {
+                $container.addClass('zuul-container-ready');
+            });
+        }
+    }
+
+    /**
+     * @return {jQuery.Promise}
+     */
+    function update() {
+        updateStart();
+        return $.ajax({
+            url: source,
+            dataType: 'json',
+            cache: false
+        })
+        .done(function (data) {
+            var html = '';
+            data = data || {};
+
+            if ('message' in data) {
+                $msg.html(data.message);
+                $msgWrap.removeClass('zuul-msg-wrap-off');
+            } else {
+                $msg.empty();
+                $msgWrap.addClass('zuul-msg-wrap-off');
+            }
+
+            $.each(data.pipelines, function (i, pipeline) {
+                html += formatPipeline(pipeline);
+            });
+
+            // Only re-parse the DOM if we have to
+            if (html !== prevHtml) {
+                prevHtml = html;
+                $('#zuul-pipelines').html(html);
+            }
+
+            $('#zuul-eventqueue-length').text(
+                 data.trigger_event_queue ? data.trigger_event_queue.length : 
'?'
+            );
+            $('#zuul-resulteventqueue-length').text(
+                 data.result_event_queue ? data.result_event_queue.length : '?'
+            );
+            updateEnd();
+
+        })
+        .fail(function (err, jqXHR, errMsg) {
+            $msg.text(source + ': ' + errMsg).show();
+            $msgWrap.removeClass('zuul-msg-wrap-off');
+            updateEnd();
+        });
+    }
+
+    function purgeGraphs() {
         $('.graph').each(function (i, img) {
             var newimg = new Image(),
                 parts = img.src.split('#');
@@ -172,14 +221,23 @@
         });
     }
 
-    $(document).ready(function () {
-        updateTimeout();
+    $(function ($) {
+        $container = $('#zuul-container');
+        $indicator = $('<span class="btn pull-right zuul-spinner">updating <i 
class="icon-refresh"></i></span>')
+            .prependTo($container);
+        $msg = $('<div class="zuul-msg alert alert-error"></div>');
+        $msgWrap = $msg
+            .wrap('<div class="zuul-msg-wrap zuul-msg-wrap-off"></div>')
+            .parent()
+            .prependTo($container);
+
+        scheduleUpdate();
 
         $(document).on({
             'show.visibility': function () {
                 enableStatusUpdates = true;
                 update();
-                updateGraphs();
+                purgeGraphs();
             },
             'hide.visibility': function () {
                 enableStatusUpdates = false;
diff --git a/shared/IntegrationPage.php b/shared/IntegrationPage.php
index b6ec7f2..936832d 100644
--- a/shared/IntegrationPage.php
+++ b/shared/IntegrationPage.php
@@ -96,8 +96,12 @@
  * Logo
  */
 .logo {
-       float: right;
-       background: #fff; /* Fix header line through logo */
+       vertical-align: middle;
+       margin-right: 1em;
+}
+
+.page-header {
+       overflow: hidden;
 }
 ');
 
@@ -166,11 +170,11 @@
 <div class="page-wrap">
        <div class="container">
                <div class="page-header">
-                       <a href="//www.wikimedia.org/">
-                               <img 
src="//upload.wikimedia.org/wikipedia/foundation/9/9a/Wikimediafoundation-logo.png"
-                                       width="135" height="135" title="Visit 
Wikimedia.org" alt="Wikimedia Foundation logo" class="logo">
-                       </a>
-                       <h2><?php echo htmlentities( $this->pageName ); ?></h2>
+                       <h2>
+                               <a href="//www.wikimedia.org/"><img 
src="//upload.wikimedia.org/wikipedia/commons/thumb/1/12/Wikimedia_logo_text_RGB.svg/120px-Wikimedia_logo_text_RGB.svg.png"
+                                       width="120" height="120" title="Visit 
Wikimedia.org" alt="Wikimedia Foundation logo" class="logo"></a>
+                               <?php echo htmlentities( $this->pageName ); ?>
+                       </h2>
                </div>
 <?php echo "\t\t" . implode( "\n\t\t", explode( "\n", $this->content ) ) . 
"\n"; ?>
        </div><!-- /.container -->

-- 
To view, visit https://gerrit.wikimedia.org/r/57899
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ifa93f856f29e1a2fe7b27e0b5a9d7e43935d8acc
Gerrit-PatchSet: 1
Gerrit-Project: integration/docroot
Gerrit-Branch: master
Gerrit-Owner: Krinkle <ttij...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to