Accessing Trac from build.webkit.org/dashboard should enable XMLHttpRequest.withCrede...
[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, options)
27 {
28     BaseObject.call(this);
29
30     console.assert(baseURL);
31
32     this.baseURL = baseURL;
33     this._needsAuthentication = (typeof options === "object") && options[Trac.NeedsAuthentication] === true;
34
35     this.recordedCommits = []; // Will be sorted in ascending order.
36
37     this.update();
38     this.updateTimer = setInterval(this.update.bind(this), Trac.UpdateInterval);
39 };
40
41 BaseObject.addConstructorFunctions(Trac);
42
43 Trac.NeedsAuthentication = "needsAuthentication";
44 Trac.UpdateInterval = 45000; // 45 seconds
45
46 Trac.Event = {
47     NewCommitsRecorded: "new-commits-recorded"
48 };
49
50 Trac.prototype = {
51     constructor: Trac,
52     __proto__: BaseObject.prototype,
53
54     get latestRecordedRevisionNumber()
55     {
56         if (!this.recordedCommits.length)
57             return undefined;
58         return this.recordedCommits[this.recordedCommits.length - 1].revisionNumber;
59     },
60
61     revisionURL: function(revision)
62     {
63         return this.baseURL + "changeset/" + encodeURIComponent(revision);
64     },
65
66     _xmlTimelineURL: function()
67     {
68         return this.baseURL + "timeline?changeset=on&max=50&format=rss";
69     },
70
71     _convertCommitInfoElementToObject: function(doc, commitElement)
72     {
73         var link = doc.evaluate("./link", commitElement, null, XPathResult.STRING_TYPE).stringValue;
74         var revisionNumber = parseInt(/\d+$/.exec(link))
75
76         function tracNSResolver(prefix)
77         {
78             if (prefix == "dc")
79                 return "http://purl.org/dc/elements/1.1/";
80             return null;
81         }
82
83         var author = doc.evaluate("./author|dc:creator", commitElement, tracNSResolver, XPathResult.STRING_TYPE).stringValue;
84         var date = doc.evaluate("./pubDate", commitElement, null, XPathResult.STRING_TYPE).stringValue;
85         date = new Date(Date.parse(date));
86         var description = doc.evaluate("./description", commitElement, null, XPathResult.STRING_TYPE).stringValue;
87
88         // The feed contains a <title>, but it's not parsed as well as what we are getting from description.
89         var parsedDescription = document.createElement("div");
90         parsedDescription.innerHTML = description;
91         var title = document.createElement("div");
92         var node = parsedDescription.firstChild.firstChild;
93         while (node && node.tagName != "BR") {
94             title.appendChild(node.cloneNode(true));
95             node = node.nextSibling;
96         }
97
98         // For some reason, trac titles start with a newline. Delete it.
99         if (title.firstChild && title.firstChild.nodeType == Node.TEXT_NODE && title.firstChild.textContent.length > 0 && title.firstChild.textContent[0] == "\n")
100             title.firstChild.textContent = title.firstChild.textContent.substring(1);
101
102         return {
103             revisionNumber: revisionNumber,
104             link: link,
105             title: title,
106             author: author,
107             date: date,
108             description: description
109         };
110     },
111
112     update: function()
113     {
114         loadXML(this._xmlTimelineURL(), function(dataDocument) {
115             var latestKnownRevision = 0;
116             if (this.recordedCommits.length)
117                 latestKnownRevision = this.recordedCommits[this.recordedCommits.length - 1].revisionNumber;
118
119             var newCommits = [];
120
121             var commitInfoElements = dataDocument.evaluate("/rss/channel/item", dataDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
122             var commitInfoElement = undefined;
123             while (commitInfoElement = commitInfoElements.iterateNext()) {
124                 var commit = this._convertCommitInfoElementToObject(dataDocument, commitInfoElement);
125                 if (commit.revisionNumber == latestKnownRevision)
126                     break;
127                 newCommits.push(commit);
128             }
129             
130             if (!newCommits.length)
131                 return;
132
133             this.recordedCommits = this.recordedCommits.concat(newCommits.reverse());
134
135             this.dispatchEventToListeners(Trac.Event.NewCommitsRecorded, {newCommits: newCommits});
136         }.bind(this), this._needsAuthentication ? { withCredentials: true } : {});
137     }
138 };