Please display information about pending runs in build.webkit.org/dashboard
[WebKit-https.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / dashboard / Scripts / Trac.js
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 Trac = function(baseURL)
27 {
28     BaseObject.call(this);
29
30     console.assert(baseURL);
31
32     this.baseURL = baseURL;
33     this.recordedCommits = []; // Will be sorted in ascending order.
34
35     this.update();
36     this.updateTimer = setInterval(this.update.bind(this), Trac.UpdateInterval);
37 };
38
39 BaseObject.addConstructorFunctions(Trac);
40
41 Trac.UpdateInterval = 45000; // 45 seconds
42
43 Trac.Event = {
44     NewCommitsRecorded: "new-commits-recorded"
45 };
46
47 Trac.prototype = {
48     constructor: Trac,
49     __proto__: BaseObject.prototype,
50
51     get latestRecordedRevisionNumber()
52     {
53         if (!this.recordedCommits.length)
54             return undefined;
55         return this.recordedCommits[this.recordedCommits.length - 1].revisionNumber;
56     },
57
58     revisionURL: function(revision)
59     {
60         return this.baseURL + "changeset/" + encodeURIComponent(revision);
61     },
62
63     _xmlTimelineURL: function()
64     {
65         return this.baseURL + "timeline?changeset=on&max=50&format=rss";
66     },
67
68     _convertCommitInfoElementToObject: function(doc, commitElement)
69     {
70         var link = doc.evaluate("./link", commitElement, null, XPathResult.STRING_TYPE).stringValue;
71         var revisionNumber = parseInt(/\d+$/.exec(link))
72
73         function tracNSResolver(prefix)
74         {
75             if (prefix == "dc")
76                 return "http://purl.org/dc/elements/1.1/";
77             return null;
78         }
79
80         var author = doc.evaluate("./author|dc:creator", commitElement, tracNSResolver, XPathResult.STRING_TYPE).stringValue;
81         var date = doc.evaluate("./pubDate", commitElement, null, XPathResult.STRING_TYPE).stringValue;
82         date = new Date(Date.parse(date));
83         var description = doc.evaluate("./description", commitElement, null, XPathResult.STRING_TYPE).stringValue;
84
85         // The feed contains a <title>, but it's not parsed as well as what we are getting from description.
86         var parsedDescription = document.createElement("div");
87         parsedDescription.innerHTML = description;
88         var title = document.createElement("div");
89         var node = parsedDescription.firstChild.firstChild;
90         while (node && node.tagName != "BR") {
91             title.appendChild(node.cloneNode(true));
92             node = node.nextSibling;
93         }
94
95         // For some reason, trac titles start with a newline. Delete it.
96         if (title.firstChild && title.firstChild.nodeType == Node.TEXT_NODE && title.firstChild.textContent.length > 0 && title.firstChild.textContent[0] == "\n")
97             title.firstChild.textContent = title.firstChild.textContent.substring(1);
98
99         return {
100             revisionNumber: revisionNumber,
101             link: link,
102             title: title,
103             author: author,
104             date: date,
105             description: description
106         };
107     },
108
109     update: function()
110     {
111         loadXML(this._xmlTimelineURL(), function(dataDocument) {
112             var latestKnownRevision = 0;
113             if (this.recordedCommits.length)
114                 latestKnownRevision = this.recordedCommits[this.recordedCommits.length - 1].revisionNumber;
115
116             var newCommits = [];
117
118             var commitInfoElements = dataDocument.evaluate("/rss/channel/item", dataDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
119             var commitInfoElement = undefined;
120             while (commitInfoElement = commitInfoElements.iterateNext()) {
121                 var commit = this._convertCommitInfoElementToObject(dataDocument, commitInfoElement);
122                 if (commit.revisionNumber == latestKnownRevision)
123                     break;
124                 newCommits.push(commit);
125             }
126             
127             if (!newCommits.length)
128                 return;
129
130             this.recordedCommits = this.recordedCommits.concat(newCommits.reverse());
131
132             this.dispatchEventToListeners(Trac.Event.NewCommitsRecorded, {newCommits: newCommits});
133         }.bind(this));
134     }
135 };