<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[162373] trunk/Tools</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/162373">162373</a></dd>
<dt>Author</dt> <dd>ap@apple.com</dd>
<dt>Date</dt> <dd>2014-01-20 13:33:51 -0800 (Mon, 20 Jan 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>build.webkit.org/dashboard should display information about patches in EWS
https://bugs.webkit.org/show_bug.cgi?id=127006

Reviewed by Ryosuke Niwa.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html:
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js: Added.
(Bugzilla.prototype.detailsURLForAttachment):
Added a class for Bugzilla. So far, the only thing it can do is build patch URLs,
which is needed when one wants to do something with a patch EWS is stuck on.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Initialization.js:
Create a Bugzilla instance.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWS.js:
(EWS.prototype.jsonQueueLengthURL):
(EWS.prototype.jsonQueueStatusURL):
Build JSON ULRs here, not in EWSQueue, as this is how other classes are structured.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueue.js:
(EWSQueue.prototype.get statusPageURL): Changed to use a URL provided by EWS instead
of second guessing.
(EWSQueue.prototype.get chartsPageURL): Added.
(EWSQueue.prototype.get loadedDetailedStatus): Tells whether we currently have
additional data already loaded (it's reset with every update).
(EWSQueue.prototype.get patches): Get patch queue.
(EWSQueue.prototype.get bots): Get bots.
(EWSQueue.prototype.update): Changed to use a specialized cheaper URL.
(EWSQueue.prototype.loadDetailedStatus): Load and transform detailed status JSON.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueueView.js:
(EWSQueueView.prototype.update.appendQueue): Add a popover is there are any patches
in the queue.
(EWSQueueView.prototype.addLinkToRow): A helper to build the popover.
(EWSQueueView.prototype.addTextToRow): Ditto.
(EWSQueueView.prototype._addQueueHeadingToPopover): Ditto.
(EWSQueueView.prototype._addBotsHeadingToPopover): Ditto.
(EWSQueueView.prototype._addDividerToPopover): Ditto.
(EWSQueueView.prototype._timeIntervalString): A helper to format a timestamp into a
relative string.
(EWSQueueView.prototype._popoverContentForEWSQueue): Build the popover.
(EWSQueueView.prototype._presentPopoverForEWSQueue): Start loading data, and present
it when done.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css:
Added rules for EWS popover. Removed a duplicate rule for build-logs-popover.
Changed a few difficult to read padding styles to padding-left.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsEWSjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWS.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsEWSQueuejs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueue.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsEWSQueueViewjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueueView.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsInitializationjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Initialization.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesQueueViewcss">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardindexhtml">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBugzillajs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBugzillajs"></a>
<div class="addfile"><h4>Added: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js (0 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js                                (rev 0)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+Bugzilla = function()
+{
+    BaseObject.call(this);
+
+    this.baseURL = &quot;https://bugs.webkit.org/&quot;;
+};
+
+BaseObject.addConstructorFunctions(Bugzilla);
+
+Bugzilla.prototype = {
+    constructor: Bugzilla,
+    __proto__: BaseObject.prototype,
+
+    detailsURLForAttachment: function(attachmentID)
+    {
+        return this.baseURL + &quot;attachment.cgi?id=&quot; + encodeURIComponent(attachmentID) + &quot;&amp;action=edit&quot;;
+    },
+};
</ins><span class="cx">Property changes on: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsEWSjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWS.js (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWS.js        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWS.js        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -49,4 +49,14 @@
</span><span class="cx"> EWS.prototype = {
</span><span class="cx">     constructor: EWS,
</span><span class="cx">     __proto__: BaseObject.prototype,
</span><ins>+
+    jsonQueueLengthURL: function(queueID)
+    {
+        return this.baseURL + &quot;queue-length-json/&quot; + encodeURIComponent(queueID) + &quot;-ews&quot;;
+    },
+
+    jsonQueueStatusURL: function(queueID)
+    {
+        return this.baseURL + &quot;queue-status-json/&quot; + encodeURIComponent(queueID) + &quot;-ews&quot;;
+    },
</ins><span class="cx"> };
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsEWSQueuejs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueue.js (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueue.js        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueue.js        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -49,14 +49,14 @@
</span><span class="cx">     constructor: EWSQueue,
</span><span class="cx">     __proto__: BaseObject.prototype,
</span><span class="cx"> 
</span><del>-    get baseURL()
</del><ins>+    get statusPageURL()
</ins><span class="cx">     {
</span><del>-        return this.ews.baseURL + &quot;queue-status-json/&quot; + encodeURIComponent(this.id) + &quot;-ews&quot;;
</del><ins>+        return this._statusPageURL;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><del>-    get statusPage()
</del><ins>+    get chartsPageURL()
</ins><span class="cx">     {
</span><del>-        return this.ews.baseURL + &quot;queue-status/&quot; + encodeURIComponent(this.id) + &quot;-ews&quot;;
</del><ins>+        return this._chartsPageURL;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     get patchCount()
</span><span class="lines">@@ -64,15 +64,81 @@
</span><span class="cx">         return this._patchCount;
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    get loadedDetailedStatus()
+    {
+        return this._loadedDetailedStatus;
+    },
+
+    get patches()
+    {
+        console.assert(this._loadedDetailedStatus);
+        return this._queue;
+    },
+
+    get bots()
+    {
+        console.assert(this._loadedDetailedStatus);
+        return this._bots;
+    },
+
</ins><span class="cx">     update: function()
</span><span class="cx">     {
</span><del>-        JSON.load(this.baseURL, function(data) {
-            var newPatchCount = data.queue.length;
</del><ins>+        this._loadedDetailedStatus = false;
+
+        JSON.load(this.ews.jsonQueueLengthURL(this.id), function(data) {
+            var newPatchCount = data.queue_length;
</ins><span class="cx">             if (this._patchCount == newPatchCount)
</span><span class="cx">                 return;
</span><span class="cx">             this._patchCount = newPatchCount;
</span><del>-
</del><span class="cx">             this.dispatchEventToListeners(EWSQueue.Event.Updated, null);
</span><span class="cx">         }.bind(this));
</span><del>-    }
</del><ins>+    },
+
+    loadDetailedStatus: function(callback)
+    {
+        JSON.load(this.ews.jsonQueueStatusURL(this.id), function(data) {
+            this._queue = [];
+            for (var i = 0, end = data.queue.length; i &lt; end; ++i) {
+                var patch = data.queue[i];
+                var activeSinceTime = patch.active_since ? Date.parse(patch.active_since) : 0;
+                this._queue.push({
+                    attachmentID: patch.attachment_id,
+                    statusPageURL: patch.status_page,
+                    latestMessage: patch.latest_message,
+                    latestMessageTime: patch.latest_message_time ? new Date(patch.latest_message_time) : null,
+                    detailedResultsURLForLatestMessage: patch.latest_results,
+                    messageCount: patch.message_count,
+                    active: patch.active,
+                    activeSince: new Date(activeSinceTime),
+                });
+            }
+
+            this._bots = [];
+            for (var i = 0, end = data.bots.length; i &lt; end; ++i) {
+                var bot = data.bots[i];
+                var latestMessageTime = bot.latest_message_time ? Date.parse(bot.latest_message_time) : 0;
+
+                var oneDayInMilliseconds = 24 * 60 * 60 * 1000;
+                var botIsCurrentlyActive = Date.now() &lt; latestMessageTime + oneDayInMilliseconds;
+                if (!botIsCurrentlyActive)
+                    continue;
+
+                // Sometimes (rarely), there are status messages with an empty bot name added to the database.
+                if (!bot.bot_id.length)
+                    bot.bot_id = &quot;&lt;empty name&gt;&quot;;
+
+                this._bots.push({
+                    id: bot.bot_id,
+                    statusPageURL: bot.status_page,
+                    latestMessageTime: new Date(latestMessageTime),
+                });
+            }
+
+            this._statusPageURL = data.status_page;
+            this._chartsPageURL = data.charts_page;
+
+            this._loadedDetailedStatus = true;
+            callback();
+        }.bind(this));
+    },
</ins><span class="cx"> };
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsEWSQueueViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueueView.js (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueueView.js        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueueView.js        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -54,18 +54,21 @@
</span><span class="cx"> 
</span><span class="cx">         function appendQueue(queue)
</span><span class="cx">         {
</span><del>-            var releaseLabel = document.createElement(&quot;a&quot;);
-            releaseLabel.classList.add(&quot;queueLabel&quot;);
-            releaseLabel.textContent = queue.title;
-            releaseLabel.href = queue.statusPage;
-            releaseLabel.target = &quot;_blank&quot;;
-            this.element.appendChild(releaseLabel);
</del><ins>+            var queueLabel = document.createElement(&quot;a&quot;);
+            queueLabel.classList.add(&quot;queueLabel&quot;);
+            queueLabel.textContent = queue.title;
+            queueLabel.href = queue.statusPageURL;
+            queueLabel.target = &quot;_blank&quot;;
+            this.element.appendChild(queueLabel);
</ins><span class="cx"> 
</span><span class="cx">             var patchCount = queue.patchCount;
</span><span class="cx"> 
</span><span class="cx">             var message = patchCount === 1 ? &quot;patch in queue&quot; : &quot;patches in queue&quot;;
</span><span class="cx">             var status = new StatusLineView(message, StatusLineView.Status.Neutral, null, patchCount || &quot;0&quot;);
</span><span class="cx">             this.element.appendChild(status.element);
</span><ins>+
+            if (patchCount &gt; 0)
+                new PopoverTracker(status.statusBubbleElement, this._presentPopoverForEWSQueue.bind(this), queue);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         this.queues.forEach(function(queue) {
</span><span class="lines">@@ -73,6 +76,149 @@
</span><span class="cx">         }, this);
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    addLinkToRow: function(rowElement, className, text, url)
+    {
+        var linkElement = document.createElement(&quot;a&quot;);
+        linkElement.className = className;
+        linkElement.textContent = text;
+        linkElement.href = url;
+        linkElement.target = &quot;_blank&quot;;
+        rowElement.appendChild(linkElement);
+    },
+
+    addTextToRow: function(rowElement, className, text)
+    {
+        var spanElement = document.createElement(&quot;span&quot;);
+        spanElement.className = className;
+        spanElement.textContent = text;
+        rowElement.appendChild(spanElement);
+    },
+
+    _addQueueHeadingToPopover: function(queue, content)
+    {
+        var title = document.createElement(&quot;div&quot;);
+        title.className = &quot;popover-queue-heading&quot;;
+
+        this.addTextToRow(title, &quot;queue-name&quot;, queue.id + &quot; ews queue&quot;);
+        this.addLinkToRow(title, &quot;queue-status-link&quot;, &quot;status page&quot;, queue.statusPageURL);
+        this.addLinkToRow(title, &quot;queue-charts-link&quot;, &quot;charts&quot;, queue.chartsPageURL);
+
+        content.appendChild(title);
+    },
+
+    _addBotsHeadingToPopover: function(queue, content)
+    {
+        var title = document.createElement(&quot;div&quot;);
+        title.className = &quot;popover-bots-heading&quot;;
+        title.textContent = &quot;latest bot event&quot;;
+        content.appendChild(title);
+    },
+
+    _addDividerToPopover: function(content)
+    {
+        var divider = document.createElement(&quot;div&quot;);
+        divider.className = &quot;divider&quot;;
+        content.appendChild(divider);
+    },
+
+    _timeIntervalString: function(time)
+    {
+        var secondsInHour = 60 * 60;
+        var timeDifference = (Date.now() - time.getTime()) / 1000;
+        var hours = Math.floor(timeDifference / secondsInHour);
+        var minutes = Math.floor((timeDifference - hours * secondsInHour) / 60);
+        var hoursPart = &quot;&quot;;
+        if (hours === 1)
+            hoursPart = &quot;1\xa0hour and &quot;;
+        else if (hours &gt; 0)
+            hoursPart = hours + &quot;\xa0hours and &quot;;
+        if (!minutes)
+            return &quot;less than a minute&quot;;
+        if (minutes === 1)
+            return hoursPart + &quot;1\xa0minute&quot;;
+        return hoursPart + minutes + &quot;\xa0minutes&quot;;
+    },
+
+    _popoverContentForEWSQueue: function(queue)
+    {
+        var content = document.createElement(&quot;div&quot;);
+        content.className = &quot;ews-popover&quot;;
+
+        this._addQueueHeadingToPopover(queue, content);
+        this._addDividerToPopover(content);
+
+        var patches = queue.patches;
+        for (var i = 0, end = patches.length; i &lt; end; ++i) {
+            var patch = patches[i];
+
+            var rowElement = document.createElement(&quot;div&quot;);
+
+            this.addLinkToRow(rowElement, &quot;patch-details-link&quot;, patch.attachmentID, patch.statusPageURL);
+
+            if (patch.messageCount)
+                this.addTextToRow(rowElement, &quot;failure-count&quot;, patch.messageCount + &quot;\xa0&quot; + (patch.messageCount === 1 ? &quot;attempt&quot; : &quot;attempts&quot;));
+
+            if (patch.detailedResultsURLForLatestMessage)
+                this.addLinkToRow(rowElement, &quot;latest-status-with-link&quot;, patch.latestMessage, patch.detailedResultsURLForLatestMessage);
+            else if (patch.latestMessage &amp;&amp; patch.latestMessage.length)
+                this.addTextToRow(rowElement, &quot;latest-status-no-link&quot;, patch.latestMessage);
+            else if (patch.active) {
+                this.addTextToRow(rowElement, &quot;latest-status-no-link&quot;, &quot;Started&quot;);
+                this.addTextToRow(rowElement, &quot;time-since-message&quot;, this._timeIntervalString(patch.activeSince) + &quot;\xa0ago&quot;);
+            } else
+                this.addTextToRow(rowElement, &quot;latest-status-no-link&quot;, &quot;Not started yet&quot;);
+
+            if (patch.latestMessageTime)
+                this.addTextToRow(rowElement, &quot;time-since-message&quot;, this._timeIntervalString(patch.latestMessageTime) + &quot;\xa0ago&quot;);
+
+            this.addLinkToRow(rowElement, &quot;bugzilla-link&quot;, &quot;bugzilla&quot;, bugzilla.detailsURLForAttachment(patch.attachmentID));
+
+            content.appendChild(rowElement);
+        }
+
+        this._addDividerToPopover(content);
+        this._addBotsHeadingToPopover(queue, content);
+        this._addDividerToPopover(content);
+
+        var bots = queue.bots;
+        for (var i = 0, end = bots.length; i &lt; end; ++i) {
+            var bot = bots[i];
+
+            var rowElement = document.createElement(&quot;div&quot;);
+
+            this.addLinkToRow(rowElement, &quot;bot-status-link&quot;, bot.id, bot.statusPageURL);
+            this.addTextToRow(rowElement, &quot;bot-status-description&quot;, this._timeIntervalString(bot.latestMessageTime) + &quot;\xa0ago&quot;);
+
+            content.appendChild(rowElement);
+        }
+
+        return content;
+    },
+
+    _presentPopoverForEWSQueue: function(element, popover, queue)
+    {
+        if (queue.loadedDetailedStatus)
+            var content = this._popoverContentForEWSQueue(queue);
+        else {
+            var content = document.createElement(&quot;div&quot;);
+            content.className = &quot;ews-popover&quot;;
+
+            var loadingIndicator = document.createElement(&quot;div&quot;);
+            loadingIndicator.className = &quot;loading-indicator&quot;;
+            loadingIndicator.textContent = &quot;Loading\u2026&quot;;
+            content.appendChild(loadingIndicator);
+
+            queue.loadDetailedStatus(function() {
+                popover.content = this._popoverContentForEWSQueue(queue);
+            }.bind(this));
+        }
+
+        var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
+        popover.content = content;
+        popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
+        return true;
+    },
+
</ins><span class="cx">     _updateQueues: function()
</span><span class="cx">     {
</span><span class="cx">         this.queues.forEach(function(queue) { queue.update(); });
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsInitializationjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Initialization.js (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Initialization.js        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Initialization.js        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -26,5 +26,6 @@
</span><span class="cx"> var settings = new Settings;
</span><span class="cx"> var buildbot = new WebKitBuildbot;
</span><span class="cx"> var webkitTrac = new Trac(&quot;http://trac.webkit.org/&quot;);
</span><ins>+var bugzilla = new Bugzilla;
</ins><span class="cx"> var ews = new EWS;
</span><span class="cx"> var testHistory = new TestHistory;
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesQueueViewcss"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -75,14 +75,6 @@
</span><span class="cx">     padding: 1px 6px 1px 6px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.build-logs-popover {
-    font-family: &quot;HelveticaNeue-Light&quot;, &quot;Helvetica Neue&quot;, sans-serif;
-    color: rgb(145, 135, 95);
-    font-size: 12px;
-    text-align: left;
-    padding: 1px 6px 1px 6px;
-}
-
</del><span class="cx"> .build-logs-popover .build-logs-heading {
</span><span class="cx">     display: inline;
</span><span class="cx"> }
</span><span class="lines">@@ -115,15 +107,42 @@
</span><span class="cx"> 
</span><span class="cx"> .test-results-popover .failure-kind-indicator {
</span><span class="cx">     color: rgb(191, 67, 41);
</span><del>-    padding: 0px 0px 0px 7px;
</del><ins>+    padding-left: 7px;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .test-results-popover .test-history-link {
</span><span class="cx">     color: rgb(145, 135, 95);
</span><del>-    padding: 0px 0px 0px 7px;
</del><ins>+    padding-left: 7px;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .test-results-popover .additional-link {
</span><span class="cx">     color: rgb(145, 135, 95);
</span><del>-    padding: 0px 0px 0px 7px;
</del><ins>+    padding-left: 7px;
</ins><span class="cx"> }
</span><ins>+
+.ews-popover {
+    font-family: &quot;HelveticaNeue-Light&quot;, &quot;Helvetica Neue&quot;, sans-serif;
+    color: rgb(145, 135, 95);
+    font-size: 12px;
+    text-align: left;
+    padding: 1px 6px 1px 6px;
+}
+
+.ews-popover .popover-queue-heading .queue-status-link,
+.ews-popover .popover-queue-heading .queue-charts-link {
+    color: rgb(145, 135, 95);
+    padding-left: 7px;
+}
+
+.ews-popover .latest-status-with-link,
+.ews-popover .latest-status-no-link {
+    color: black;
+    padding-left: 7px;
+}
+
+.ews-popover .failure-count,
+.ews-popover .time-since-message,
+.ews-popover .bugzilla-link,
+.ews-popover .bot-status-description {
+    padding-left: 7px;
+}
</ins></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx">     &lt;script src=&quot;Scripts/BaseObject.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/Dashboard.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/Buildbot.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Scripts/Bugzilla.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Scripts/EWS.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/WebKitBuildbot.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/BuildbotQueue.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (162372 => 162373)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2014-01-20 21:29:59 UTC (rev 162372)
+++ trunk/Tools/ChangeLog        2014-01-20 21:33:51 UTC (rev 162373)
</span><span class="lines">@@ -1,5 +1,55 @@
</span><span class="cx"> 2014-01-20  Alexey Proskuryakov  &lt;ap@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        build.webkit.org/dashboard should display information about patches in EWS
+        https://bugs.webkit.org/show_bug.cgi?id=127006
+
+        Reviewed by Ryosuke Niwa.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html:
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Bugzilla.js: Added.
+        (Bugzilla.prototype.detailsURLForAttachment):
+        Added a class for Bugzilla. So far, the only thing it can do is build patch URLs,
+        which is needed when one wants to do something with a patch EWS is stuck on.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Initialization.js:
+        Create a Bugzilla instance.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWS.js:
+        (EWS.prototype.jsonQueueLengthURL):
+        (EWS.prototype.jsonQueueStatusURL):
+        Build JSON ULRs here, not in EWSQueue, as this is how other classes are structured.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueue.js:
+        (EWSQueue.prototype.get statusPageURL): Changed to use a URL provided by EWS instead
+        of second guessing.
+        (EWSQueue.prototype.get chartsPageURL): Added.
+        (EWSQueue.prototype.get loadedDetailedStatus): Tells whether we currently have
+        additional data already loaded (it's reset with every update).
+        (EWSQueue.prototype.get patches): Get patch queue.
+        (EWSQueue.prototype.get bots): Get bots.
+        (EWSQueue.prototype.update): Changed to use a specialized cheaper URL.
+        (EWSQueue.prototype.loadDetailedStatus): Load and transform detailed status JSON.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/EWSQueueView.js:
+        (EWSQueueView.prototype.update.appendQueue): Add a popover is there are any patches
+        in the queue.
+        (EWSQueueView.prototype.addLinkToRow): A helper to build the popover.
+        (EWSQueueView.prototype.addTextToRow): Ditto.
+        (EWSQueueView.prototype._addQueueHeadingToPopover): Ditto.
+        (EWSQueueView.prototype._addBotsHeadingToPopover): Ditto.
+        (EWSQueueView.prototype._addDividerToPopover): Ditto.
+        (EWSQueueView.prototype._timeIntervalString): A helper to format a timestamp into a
+        relative string.
+        (EWSQueueView.prototype._popoverContentForEWSQueue): Build the popover.
+        (EWSQueueView.prototype._presentPopoverForEWSQueue): Start loading data, and present
+        it when done.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css:
+        Added rules for EWS popover. Removed a duplicate rule for build-logs-popover.
+        Changed a few difficult to read padding styles to padding-left.
+
+2014-01-20  Alexey Proskuryakov  &lt;ap@apple.com&gt;
+
</ins><span class="cx">         Fix webkitpy tests.
</span><span class="cx"> 
</span><span class="cx">         * Scripts/webkitpy/common/net/web_mock.py: (MockBrowser.set_handle_robots):
</span></span></pre>
</div>
</div>

</body>
</html>