OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
12 * | 12 * |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | 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, | 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 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 | 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 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 | 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 * THE POSSIBILITY OF SUCH DAMAGE. | 23 * THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 var ui = ui || {}; | 26 var ui = ui || {}; |
27 | 27 |
28 (function () { | 28 (function () { |
29 | 29 |
30 ui.displayURLForBuilder = function(builderName) | 30 // FIXME: Put this all in a more appropriate place. |
31 { | |
32 return config.waterfallURL + '?' + base.queryParam({ | |
33 'builder': builderName | |
34 }); | |
35 } | |
36 | |
37 ui.kUseNewWindowForLinksSetting = 'gardenomatic.use-new-window-for-links'; | |
38 | 31 |
39 ui.displayNameForBuilder = function(builderName) | 32 ui.displayNameForBuilder = function(builderName) |
40 { | 33 { |
41 return builderName.replace(/Webkit /i, ''); | 34 return builderName.replace(/Webkit /i, ''); |
42 } | 35 } |
43 | 36 |
44 ui.urlForCrbug = function(bugID) | |
45 { | |
46 return 'http://crbug.com/' + bugID; | |
47 } | |
48 | |
49 ui.urlForFlakinessDashboard = function(opt_testNameList) | 37 ui.urlForFlakinessDashboard = function(opt_testNameList) |
50 { | 38 { |
51 var testsParameter = opt_testNameList ? opt_testNameList.join(',') : ''; | 39 var testsParameter = opt_testNameList ? opt_testNameList.join(',') : ''; |
52 return 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#
tests=' + encodeURIComponent(testsParameter); | 40 return 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#
tests=' + encodeURIComponent(testsParameter); |
53 } | 41 } |
54 | 42 |
55 ui.urlForEmbeddedFlakinessDashboard = function(opt_testNameList) | 43 ui.urlForEmbeddedFlakinessDashboard = function(opt_testNameList) |
56 { | 44 { |
57 return ui.urlForFlakinessDashboard(opt_testNameList) + '&showChrome=false'; | 45 return ui.urlForFlakinessDashboard(opt_testNameList) + '&showChrome=false'; |
58 } | 46 } |
59 | 47 |
60 ui.setTargetForLink = function(anchor) | |
61 { | |
62 if (anchor.href.indexOf('#') === 0) | |
63 return; | |
64 if (ui.useNewWindowForLinks) | |
65 anchor.target = '_blank'; | |
66 else | |
67 anchor.removeAttribute('target'); | |
68 } | |
69 | |
70 ui.setUseNewWindowForLinks = function(enabled) | |
71 { | |
72 ui.useNewWindowForLinks = enabled; | |
73 if (enabled) | |
74 localStorage[ui.kUseNewWindowForLinksSetting] = 'true'; | |
75 else | |
76 delete localStorage[ui.kUseNewWindowForLinksSetting]; | |
77 | |
78 [].forEach.call(document.querySelectorAll('a'), function(link) { | |
79 ui.setTargetForLink(link); | |
80 }); | |
81 } | |
82 ui.setUseNewWindowForLinks(!!localStorage[ui.kUseNewWindowForLinksSetting]); | |
83 | |
84 ui.createLinkNode = function(url, textContent) | |
85 { | |
86 var link = document.createElement('a'); | |
87 link.href = url; | |
88 ui.setTargetForLink(link); | |
89 link.appendChild(document.createTextNode(textContent)); | |
90 return link; | |
91 } | |
92 | |
93 ui.onebar = base.extends('div', { | |
94 init: function() | |
95 { | |
96 this.id = 'onebar'; | |
97 this.innerHTML = | |
98 '<ul>' + | |
99 '<li><a href="#unexpected">Unexpected Failures</a></li>' + | |
100 '<li><a href="#results">Results</a></li>' + | |
101 '</ul>' + | |
102 '<div id="link-handling"><input type="checkbox" id="new-window-for-l
inks"><label for="new-window-for-links">Open links in new window</label></div>'
+ | |
103 '<div id="unexpected"></div>' + | |
104 '<div id="results"></div>'; | |
105 this._tabNames = [ | |
106 'unexpected', | |
107 'results', | |
108 ] | |
109 | |
110 this._tabIndexToSavedScrollOffset = {}; | |
111 this._tabs = $(this).tabs({ | |
112 disabled: [this._tabNames.indexOf('results')], | |
113 show: function(event, ui) { this._restoreScrollOffset(ui.index); }, | |
114 select: function(event, ui) { | |
115 this._saveScrollOffset(); | |
116 window.location.hash = ui.tab.hash; | |
117 }.bind(this) | |
118 }); | |
119 }, | |
120 _saveScrollOffset: function() { | |
121 var tabIndex = this._tabs.tabs('option', 'selected'); | |
122 this._tabIndexToSavedScrollOffset[tabIndex] = document.body.scrollTop; | |
123 }, | |
124 _restoreScrollOffset: function(tabIndex) | |
125 { | |
126 document.body.scrollTop = this._tabIndexToSavedScrollOffset[tabIndex] ||
0; | |
127 }, | |
128 _setupHistoryHandlers: function() | |
129 { | |
130 function currentHash() { | |
131 var hash = window.location.hash; | |
132 return (!hash || hash == '#') ? '#unexpected' : hash; | |
133 } | |
134 | |
135 var self = this; | |
136 window.onhashchange = function(event) { | |
137 var tabName = currentHash().substring(1); | |
138 self._selectInternal(tabName); | |
139 }; | |
140 | |
141 // When navigating from the browser chrome, we'll | |
142 // scroll to the #tabname contents. popstate fires before | |
143 // we scroll, so we can save the scroll offset first. | |
144 window.onpopstate = function() { | |
145 self._saveScrollOffset(); | |
146 }; | |
147 }, | |
148 _setupLinkSettingHandler: function() | |
149 { | |
150 if (ui.useNewWindowForLinks) | |
151 document.getElementById('new-window-for-links').setAttribute('checke
d', true); | |
152 document.getElementById('new-window-for-links').addEventListener('change
', function(event) { | |
153 ui.setUseNewWindowForLinks(this.checked); | |
154 }); | |
155 }, | |
156 attach: function() | |
157 { | |
158 document.body.insertBefore(this, document.body.firstChild); | |
159 this._setupLinkSettingHandler(); | |
160 this._setupHistoryHandlers(); | |
161 }, | |
162 tabNamed: function(tabName) | |
163 { | |
164 if (this._tabNames.indexOf(tabName) == -1) | |
165 return null; | |
166 tab = document.getElementById(tabName); | |
167 // We perform this sanity check below to make sure getElementById | |
168 // hasn't given us a node in some other unrelated part of the document. | |
169 // that shouldn't happen normally, but it could happen if an attacker | |
170 // has somehow sneakily added a node to our document. | |
171 if (tab.parentNode != this) | |
172 return null; | |
173 return tab; | |
174 }, | |
175 unexpected: function() | |
176 { | |
177 return this.tabNamed('unexpected'); | |
178 }, | |
179 results: function() | |
180 { | |
181 return this.tabNamed('results'); | |
182 }, | |
183 _selectInternal: function(tabName) { | |
184 var tabIndex = this._tabNames.indexOf(tabName); | |
185 this._tabs.tabs('enable', tabIndex); | |
186 this._tabs.tabs('select', tabIndex); | |
187 }, | |
188 select: function(tabName) | |
189 { | |
190 this._saveScrollOffset(); | |
191 this._selectInternal(tabName); | |
192 window.location = '#' + tabName; | |
193 } | |
194 }); | |
195 | |
196 ui.TreeStatus = base.extends('div', { | |
197 addStatus: function(name) | |
198 { | |
199 var label = document.createElement('div'); | |
200 label.textContent = " " + name + ' status: '; | |
201 this.appendChild(label); | |
202 var statusSpan = document.createElement('span'); | |
203 statusSpan.textContent = '(Loading...) '; | |
204 label.appendChild(statusSpan); | |
205 treestatus.fetchTreeStatus(treestatus.urlByName(name), statusSpan); | |
206 }, | |
207 init: function() | |
208 { | |
209 this.className = 'treestatus'; | |
210 this.addStatus('blink'); | |
211 this.addStatus('chromium'); | |
212 }, | |
213 }); | |
214 | |
215 ui.revisionDetails = base.extends('span', { | |
216 // We only support 2 levels of visual escalation levels: warning and critica
l. | |
217 warnRollRevisionSpanThreshold: 45, | |
218 criticalRollRevisionSpanThreshold: 80, | |
219 classNameForUrgencyLevel: function(rollRevisionSpan) { | |
220 if (rollRevisionSpan < this.criticalRollRevisionSpanThreshold) | |
221 return "warning"; | |
222 return "critical"; | |
223 }, | |
224 updateUI: function(totRevision) { | |
225 this.appendChild(document.createElement("br")); | |
226 this.appendChild(document.createTextNode('Last roll is to ')); | |
227 this.appendChild(ui.createLinkNode(trac.changesetURL(this.lastRolledRevi
sion), this.lastRolledRevision)); | |
228 var rollRevisionSpan = totRevision - this.lastRolledRevision; | |
229 // Don't clutter the UI if we haven't run behind. | |
230 if (rollRevisionSpan > this.warnRollRevisionSpanThreshold) { | |
231 var wrapper = document.createElement("span"); | |
232 wrapper.className = this.classNameForUrgencyLevel(rollRevisionSpan); | |
233 wrapper.appendChild(document.createTextNode("(" + rollRevisionSpan +
" revisions behind)")); | |
234 this.appendChild(wrapper); | |
235 } | |
236 this.appendChild(document.createTextNode(', current autoroll ')); | |
237 if (this.roll) { | |
238 var linkText = "" + this.roll.fromRevision + ":" + this.roll.toRevis
ion; | |
239 this.appendChild(ui.createLinkNode(this.roll.url, linkText)); | |
240 if (this.roll.isStopped) | |
241 this.appendChild(document.createTextNode(' (STOPPED) ')); | |
242 } else { | |
243 this.appendChild(document.createTextNode(' None')); | |
244 } | |
245 }, | |
246 init: function() { | |
247 var theSpan = this; | |
248 theSpan.appendChild(document.createTextNode('Latest revision processed b
y every bot: ')); | |
249 | |
250 var latestRevision = model.latestRevisionWithNoBuildersInFlight(); | |
251 var latestRevisions = model.latestRevisionByBuilder(); | |
252 | |
253 // Get the list of builders sorted with the most recent one first. | |
254 var builders = Object.keys(latestRevisions); | |
255 builders.sort(function (a, b) { return parseInt(latestRevisions[b]) - pa
rseInt(latestRevisions[a]);}); | |
256 | |
257 var summaryNode = document.createElement('summary'); | |
258 var summaryLinkNode = ui.createLinkNode(trac.changesetURL(latestRevision
), latestRevision); | |
259 summaryNode.appendChild(summaryLinkNode); | |
260 | |
261 var revisionsTableNode = document.createElement('table'); | |
262 builders.forEach(function(builderName) { | |
263 var trNode = document.createElement('tr'); | |
264 | |
265 var tdNode = document.createElement('td'); | |
266 tdNode.appendChild(ui.createLinkNode(ui.displayURLForBuilder(builder
Name), builderName.replace('WebKit ', ''))); | |
267 trNode.appendChild(tdNode); | |
268 | |
269 var tdNode = document.createElement('td'); | |
270 tdNode.appendChild(document.createTextNode(latestRevisions[builderNa
me])); | |
271 trNode.appendChild(tdNode); | |
272 | |
273 revisionsTableNode.appendChild(trNode); | |
274 }); | |
275 | |
276 var revisionsNode = document.createElement('details'); | |
277 revisionsNode.appendChild(summaryNode); | |
278 revisionsNode.appendChild(revisionsTableNode); | |
279 theSpan.appendChild(revisionsNode); | |
280 | |
281 // This adds a pop-up when we hover over the summary if the details aren
't being shown. | |
282 var revisionsPopUp = document.createElement('span') | |
283 revisionsPopUp.id = 'revisionPopUp'; | |
284 summaryLinkNode.appendChild(revisionsPopUp); | |
285 revisionsPopUp.appendChild(revisionsTableNode.cloneNode(true)); | |
286 | |
287 summaryLinkNode.addEventListener('mouseover', function(event) { | |
288 if (!revisionsNode.open) { | |
289 revisionsPopUp.style.position = 'absolute'; | |
290 revisionsPopUp.style.left = summaryNode.offsetLeft + 'px'; | |
291 revisionsPopUp.style.top = (summaryNode.offsetTop + summaryNode.
offsetHeight) + 'px'; | |
292 revisionsPopUp.classList.add('active'); | |
293 } | |
294 }); | |
295 | |
296 summaryLinkNode.addEventListener('mouseout', function(event) { | |
297 if (!revisionsNode.open) { | |
298 revisionsPopUp.classList.remove("active"); | |
299 } | |
300 }); | |
301 | |
302 var totRevision = model.latestRevision(); | |
303 theSpan.appendChild(document.createTextNode(', trunk is at ')); | |
304 theSpan.appendChild(ui.createLinkNode(trac.changesetURL(totRevision), to
tRevision)); | |
305 } | |
306 }); | |
307 | |
308 })(); | 48 })(); |
OLD | NEW |