blob: 81c74b2d58632682a6b01fc76513a20bc02ac94c [file] [log] [blame]
{% extends "announce.html" %}
{% block head %}
{{ super() }}
<script type='text/javascript'>
// <![CDATA[
//
//
// Functions used to display the build status bubble on box click.
//
// show the build status box. This is called when the user clicks on a block.
function showBuildBox(url, event) {
// Find the current curson position.
var cursorPosTop = (window.event ? window.event.clientY : event.pageY)
var cursorPosLeft = (window.event ? window.event.clientX : event.pageX)
// Offset the position by 5, to make the window appears under the cursor.
cursorPosTop = cursorPosTop + document.body.scrollTop -5 ;
cursorPosLeft = cursorPosLeft + document.body.scrollLeft - 5;
// Move the div (hidden) under the cursor.
var divBox = document.getElementById('divBox');
divBox.style.top = parseInt(cursorPosTop) + 'px';
divBox.style.left = parseInt(cursorPosLeft) + 'px';
// Reload the hidden frame with the build page we want to show.
// The onload even on this frame will update the div and make it visible.
document.getElementById("frameBox").src = url
// We don't want to reload the page.
return false;
}
// OnLoad handler for the iframe containing the build to show.
function updateDiv(event) {
// Get the frame innerHTML.
var iframeContent = document.getElementById("frameBox").contentWindow.document.body.innerHTML;
// If there is any content, update the div, and make it visible.
if (iframeContent) {
var divBox = document.getElementById('divBox');
divBox.innerHTML = iframeContent ;
divBox.style.display = "block";
}
}
// Util functions to know if an element is contained inside another element.
// We use this to know when we mouse out our build status div.
function containsDOM (container, containee) {
var isParent = false;
do {
if ((isParent = container == containee))
break;
containee = containee.parentNode;
} while (containee != null);
return isParent;
}
// OnMouseOut handler. Returns true if the mouse moved out of the element.
// It is false if the mouse is still in the element, but in a blank part of it,
// like in an empty table cell.
function checkMouseLeave(element, event) {
if (element.contains && event.toElement) {
return !element.contains(event.toElement);
}
else if (event.relatedTarget) {
return !containsDOM(element, event.relatedTarget);
}
}
// Creates a new cookie.
function createCookie(name, value, day) {
var date = new Date();
date.setTime(date.getTime() + (day * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
document.cookie = name + "=" + value+expires + "; path=/";
}
// Returns the vaue of a cookie, or null if it does not exist.
function readCookie(name) {
var begin = name + "=";
var data = document.cookie.split(';');
for(var i = 0; i < data.length; i++) {
var cookie = data[i];
while (cookie.charAt(0) == ' ')
cookie = cookie.substring(1, cookie.length);
if (cookie.indexOf(begin) == 0)
return cookie.substring(begin.length, cookie.length);
}
return null;
}
// Deletes a cookie.
function eraseCookie(name) {
createCookie(name, "", -1);
}
// Enhancement to element.querySelector that supports starting
// with '>' or '+' which behave relative to the context element.
// Based on http://www.w3.org/TR/selectors-api2/.
function querySelector2(element, selector) {
var UNIQUE_ID = "unique-id-for-querySelector2";
if (!element.hasAttribute("id"))
element.id = UNIQUE_ID;
var result = document.querySelector("#" + element.id + selector);
if (element.id == UNIQUE_ID)
element.removeAttribute("id");
return result;
}
// Converts a string of HTML into cleaned up text suitable for a tooltip.
function htmlToText(html) {
return html.trim().replace(/\s{2,}/g, " ")
.replace(/<br>/g, "\n")
.replace(/<[^>]*>/g, "")
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&nbsp;/g, "\xa0");
}
// Hides all "details" and "comments" section.
function collapse() {
// Hide all Comments sections.
var comments = document.querySelectorAll('.DevComment');
for(var i = 0; i < comments.length; i++) {
comments[i].style.display = "none";
}
// Hide all details sections.
var details = document.querySelectorAll('.DevDetails');
for(var i = 0; i < details.length; i++) {
details[i].style.display = "none";
}
// Show comments as hover text.
var revisions = document.querySelectorAll('.DevRev');
for (var i = 0; i < revisions.length; i++) {
var tr = revisions[i].parentNode;
var comment = querySelector2(tr, "+ tr > .DevComment");
var name = querySelector2(tr, "> .DevName");
if (!comment || !name)
continue;
var text = htmlToText(comment.innerHTML);
revisions[i].title = name.title = text;
}
// Fix the rounding on the Revision box. (Lower right corner must be round)
for(var i = 0; i < revisions.length; i++) {
revisions[i].className = revisions[i].className + ' DevRevCollapse';
}
// Fix the rounding on the last category box. (Lower left corner must be round)
var status = document.querySelectorAll('.last');
for(var i = 0; i < status.length; i++) {
status[i].className = status[i].className + ' DevStatusCollapse';
}
// Create a cookie to remember that we want the view to be collapsed.
createCookie('collapsed', 'true', 30)
// Hide the collapse and the unmerge buttons.
document.querySelectorAll('.collapse')[0].style.display = 'none'
document.querySelectorAll('.unmerge')[0].style.display = 'none'
// Activate the merge and expand buttons.
document.querySelectorAll('.uncollapse')[0].style.display = 'inline'
document.querySelectorAll('.merge')[0].style.display = 'inline'
}
// Expands the view. This is the opposite of "Collapse"
function uncollapse() {
unmerge();
// Make the comments visible.
var comments = document.querySelectorAll('.DevComment');
for(var i = 0; i < comments.length; i++) {
comments[i].style.display = "";
}
// Make the details visible.
var details = document.querySelectorAll('.DevDetails');
for(var i = 0; i < details.length; i++) {
details[i].style.display = "";
}
// Stop showing comments as hover text.
var revisions = document.querySelectorAll('.DevRev');
for (var i = 0; i < revisions.length; i++) {
var name = querySelector2(revisions[i].parentNode, "> .DevName");
if (!name)
continue;
revisions[i].title = name.title = null;
}
// Remove the round corner (lower right) for the Revision box.
var revisions = document.querySelectorAll('.DevRev');
for(var i = 0; i < revisions.length; i++) {
revisions[i].className = revisions[i].className.replace('DevRevCollapse', '');
}
// Remoe the round corner (lower left) for the last category box.
var status = document.querySelectorAll('.DevStatus');
for(var i = 0; i < status.length; i++) {
status[i].className = status[i].className.replace('DevStatusCollapse', '');
}
// Delete the cookies that say that we want to be collapsed or merged.
eraseCookie('collapsed')
eraseCookie('merged')
// Display the "collapse" and "merge" buttons.
document.querySelectorAll('.collapse')[0].style.display = 'inline'
document.querySelectorAll('.merge')[0].style.display = 'inline'
// Remove the "uncollapse" and "unmerge" buttons.
document.querySelectorAll('.uncollapse')[0].style.display = 'none'
document.querySelectorAll('.unmerge')[0].style.display = 'none'
}
// Merge all the status boxes together.
function merge() {
collapse();
// Hide all the spacing.
var spacing = document.querySelectorAll('.DevStatusSpacing');
for(var i = 0; i < spacing.length; i++) {
spacing[i].style.display = "none";
}
// Each boxes have, in the className, a tag that uniquely represents the
// build where this data comes from.
// Since we want to merge all the boxes coming from the same build, we
// parse the document to find all the builds, and then, for each build, we
// concatenate the boxes.
var allTags = [];
all = document.getElementsByTagName('*')
for(var i = 0; i < all.length; i++) {
var element = all[i];
start = element.className.indexOf('Tag')
if (start != -1) {
var className = ""
end = element.className.indexOf(' ', start)
if (end != -1) {
className = element.className.substring(start, end);
} else {
className = element.className.substring(start);
}
allTags[className] = 1;
}
}
// Mergeall tags that we found
for (i in allTags) {
var current = document.querySelectorAll('.' + i);
// We do the work only if there is more than 1 box with the same
// build.
if (current.length > 1) {
// Add the noround class to all the boxes.
for(var i = 0; i < current.length; i++) {
current[i].className = current[i].className + ' noround';
}
// Add the begin class to the first box.
current[0].className = current[0].className + ' begin';
// Add the end class to the last box.
last = current.length - 1;
current[last].className = current[last].className + ' end';
}
}
// Display the "unmerge" button.
document.querySelectorAll('.unmerge')[0].style.display = 'inline'
document.querySelectorAll('.uncollapse')[0].style.display = 'inline'
// Remove the "merge" button.
document.querySelectorAll('.collapse')[0].style.display = 'none'
document.querySelectorAll('.merge')[0].style.display = 'none'
// Create a cookie to remember that we want to be merged.
createCookie('merged', 'true', 30)
}
// Un-merge the view. This is the opposite of "merge".
function unmerge() {
// We put back all the spacing.
var spacing = document.querySelectorAll('.DevStatusSpacing');
for(var i = 0; i < spacing.length; i++) {
spacing[i].style.display = "";
}
// We remove the class added to all the boxes we modified.
var noround = document.querySelectorAll('.noround');
for(var i = 0; i < noround.length; i++) {
noround[i].className = noround[i].className.replace("begin", '');
noround[i].className = noround[i].className.replace("end", '');
noround[i].className = noround[i].className.replace("noround", '');
}
// Delete the cookie, we don't want to be merged anymore.
eraseCookie('merged')
// Display the "merge" button.
document.querySelectorAll('.merge')[0].style.display = 'inline'
// Hide the "unmerge" button.
document.querySelectorAll('.unmerge')[0].style.display = 'none'
}
// Link unlinked revisions to their gerrit code review where possible.
function linkRevisions() {
var revisions = document.querySelectorAll('.DevRev');
for (var i = 0; i < revisions.length; i++) {
var tr = revisions[i].parentNode;
var comment = querySelector2(tr, "+ tr > .DevComment");
var a = revisions[i].querySelector("a");
if (!comment || !a || a.getAttribute("href"))
continue;
var re = /.*<br>Reviewed-on: (?:<a [^>]*>)?https:\/\/chrome-internal-review.googlesource.com\/(\d+)\s*(?:<\/a>)?(?:<br>|\s*$)/;
var match = re.exec(comment.innerHTML);
if (match)
a.setAttribute("href", "https://chrome-internal-review.googlesource.com/" + match[1]);
}
}
function SetupView() {
if (readCookie('merged')) {
merge();
} else if (readCookie('collapsed')) {
collapse();
}
linkRevisions();
}
document.addEventListener("DOMContentLoaded", SetupView, false);
// ]]>
</script>
{% endblock %}
{% block content %}
<div align="center" class="content_header">
<table width="95%" class="Grid" border="0" cellspacing="0">
<tr>
<td><div class="bbp_placeholder" id="{{ mastername }}"></div></td>
<td width="33%" align="left" class="left_align">
{% if repository %}
<br><b>Repository:</b> {{ repository|e }}
{% endif %}
{% if branch != ANYBRANCH %}
<br><b>Branch:</b> {{ branch|e }}
{% endif %}
</td>
<td width="33%" align="center" class="center_align">
<div align="center">
<table class="info">
<tr>
<td>Legend:&nbsp;&nbsp;</td>
<td class='legend success' title='All tests passed'>Passed</td>
<td class='legend failure' title='There is a new failure. Take a look!'>Failed</td>
<td class='legend warnings' title='It was failing before, and it is still failing. Make sure you did not introduce new regressions'>Failed&nbsp;Again</td>
<td class='legend running' title='The tests are still running'>Running</td>
<td class='legend exception' title='Something went wrong with the test, there is no result'>Exception</td>
<td class='legend offline' title='The builder is offline, as there are no slaves connected to it'>Offline</td>
<td class='legend notstarted' title='No result yet.'>No&nbsp;data</td>
</tr>
</table>
</div>
</td>
<td width="33%" align="right" class="right_align">
<script type="text/javascript">
// <![CDATA[
function reload_page() {
name_value = document.getElementById('namebox').value
if (document.location.href.lastIndexOf('?') == -1)
document.location.href = document.location.href+ '?name=' + name_value;
else
document.location.href = document.location.href+ '&name=' + name_value;
}
// ]]>
</script>
<input id='namebox' name='name' type='text' style='color:#999;'
onblur='this.value = this.value || this.defaultValue; this.style.color = "#999";'
onfocus='this.value=""; this.style.color = "#000";'
value='Personalized for...'/>
<input type='submit' value='Go' onclick='reload_page()'/>
</td>
</tr>
</table>
</div>
<br/>
{% set alt_class = cycler('', 'Alt') %}
<div align="center">
<table width="96%">
{% if categories|length > 1 %}
<tr id="groupline">
<td width="1%">
</td>
<td width="1%">
</td>
<script type="text/javascript">
function add_group_row(){
var groups = {{ categories }}
var name_to_count = {};
for(var i=0; i < groups.length; i++){
var name = groups[i].name.substr(0, groups[i].name.indexOf(','));
name_to_count[name] = (name_to_count[name] || 0) + 1;
}
var keys = Object.keys(name_to_count);
for(var k in name_to_count) {
var classname = "DevStatus Alt"
if(k == keys[0])
classname += " first";
if(k == keys[keys.length-1])
classname += " last";
x = document.getElementById('groupline').insertCell();
x.setAttribute("class", classname);
x.setAttribute("colspan", name_to_count[k]);
x.innerHTML = k;
}
}
add_group_row();
</script>
</tr>
<tr class='DevStatusSpacing'>
</tr>
{% endif %}
{% if categories|length > 1 %}
<tr>
<td width="1%">
</td>
<td width="1%">
</td>
{% for c in categories %}
<td class='DevStatus {{ "last" if loop.last else '' }}' width='{{ c.size }}%'>
{{ c.name.split(',')[1] }}
</td>
{% endfor %}
</tr>
<tr class='DevStatusSpacing'>
</tr>
{% endif %}
{% if slaves %}
<tr>
<td width="1%">
</td>
<td width="1%">
</td>
{% for c in categories %}
<td class='DevSlave Alt {{ "last" if loop.last else '' }}'>
<table width="100%">
<tr>
{% for s in slaves[c.name] %}
<td class='DevSlaveBox'>
<a href='{{ s.url }}' title='{{ s.pageTitle }}' class='DevSlaveBox
{{ s.color }}' target="_blank">
</a>
<div id="{{ mastername }}:{{ s.builderName }}"
class="bbp_placeholder"></div>
</td>
{% endfor %}
</tr>
</table>
</td>
{% endfor %}
</tr>
{% endif %}
{% for r in revisions %}
{% set alt = alt_class.next() %}
{% set last = "last" if loop.last else "" %}
<tr>
<td class='DevRev {{ alt }} DevRevCollapse' width="1%">
<a href="{{ r.link }}" target="_blank">{{ r.id|shortrev(r.repository) }}</a>
</td>
<td class='DevName {{ alt }}' width="1%">
{{ r.who|user }}
</td>
{% for c in categories %}
<td class='DevStatus {{ alt }} {% if loop.last %}DevStatusCollapse{% endif %}'>
<table width="100%">
<tr>
{% for b in r.builds[c.name] %}
<td class='DevStatusBox'>
<a href='#' onclick='showBuildBox("{{ b.url }}", event); return false;'
title='{{ b.pageTitle|e }}' class='DevStatusBox {{ b.color }} {{ b.tag }}'
target="_blank"></a>
<div id="{{ mastername }}:{{ b.builderName }}:{{ b.buildNumber }}"
class="bbp_placeholder"></div>
</td>
{% endfor %}
</tr>
</table>
</td>
{% endfor %}
</tr>
<tr>
<td colspan="{{ r.span }}" class='DevComment {{ alt }}'>
{{ r.comments|changecomment(r.project or None)|replace('\n', '<br/>')|replace(' ','&nbsp; ') }}
</td>
</tr>
{% if r.details %}
<tr>
<td colspan="{{ r.span }}" class='DevDetails {{ alt }}'>
<ul style='margin: 0px; padding: 0 0 0 1.5em;'>
{% for d in r.details %}
<li>{{ d.buildername }}: {{ d.status|truncate(255, True) }} - &nbsp;
{%- for l in d.logs -%}
<a href="{{ l.url }}"> {{ l.name }} </a>{% if not loop.last %} - {% endif %}
{%- endfor -%}
</li>
{% endfor %}
</ul>
</td>
</tr>
{% endif %}
<tr class='DevStatusSpacing'>
<td>
</td>
</tr>
{% else %}
<tr><td>No revisions available</td></tr>
{% endfor %}
</table>
</div>
<div id="divBox" onmouseout="if (checkMouseLeave(this, event)) this.style.display = 'None'" class="BuildWaterfall">
</div>
<iframe id="frameBox" style="display: none;"></iframe>
<script type="text/javascript">
// replace 'onload="updateDiv(event);" with this, as iframe doesn't have onload event in xhtml
window.onload = function() {
document.getElementById('frameBox').onload = function(event) {
updateDiv(event);
};
};
</script>
{% endblock %}
{% block footer %}
<div class="footer" style="clear:both">
<hr/>
[ <a class='collapse' href='#' OnClick='collapse(); return false;'>collapse</a>
<a class='uncollapse' href='#' OnClick='uncollapse(); return false;'>un-collapse</a>
<a class='merge' href="#" OnClick="merge(); return false;">merge</a>
<a class='unmerge' style='display: none' href="#" OnClick="unmerge(); return false;">un-merge</a> ]
</div>
{% endblock %}