2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 Trac = function(baseURL)
28 BaseObject.call(this);
30 console.assert(baseURL);
32 this.baseURL = baseURL;
33 this.recordedCommits = []; // Will be sorted in ascending order.
36 this.updateTimer = setInterval(this.update.bind(this), Trac.UpdateInterval);
39 BaseObject.addConstructorFunctions(Trac);
41 Trac.UpdateInterval = 45000; // 45 seconds
44 NewCommitsRecorded: "new-commits-recorded"
49 __proto__: BaseObject.prototype,
51 get latestRecordedRevisionNumber()
53 if (!this.recordedCommits.length)
55 return this.recordedCommits[this.recordedCommits.length - 1].revisionNumber;
58 revisionURL: function(revision)
60 return this.baseURL + "changeset/" + encodeURIComponent(revision);
63 _xmlTimelineURL: function()
65 return this.baseURL + "timeline?changeset=on&max=50&format=rss";
68 _convertCommitInfoElementToObject: function(doc, commitElement)
70 var link = doc.evaluate("./link", commitElement, null, XPathResult.STRING_TYPE).stringValue;
71 var revisionNumber = parseInt(/\d+$/.exec(link))
73 function tracNSResolver(prefix)
76 return "http://purl.org/dc/elements/1.1/";
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;
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;
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);
100 revisionNumber: revisionNumber,
105 description: description
111 loadXML(this._xmlTimelineURL(), function(dataDocument) {
112 var latestKnownRevision = 0;
113 if (this.recordedCommits.length)
114 latestKnownRevision = this.recordedCommits[this.recordedCommits.length - 1].revisionNumber;
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)
124 newCommits.push(commit);
127 if (!newCommits.length)
130 this.recordedCommits = this.recordedCommits.concat(newCommits.reverse());
132 this.dispatchEventToListeners(Trac.Event.NewCommitsRecorded, {newCommits: newCommits});