Skip to content

Instantly share code, notes, and snippets.

@mook
Created November 4, 2010 07:39
Show Gist options
  • Save mook/662211 to your computer and use it in GitHub Desktop.
Save mook/662211 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name bugzilla flow
// @namespace http://mook.moz.googlepages.com/greasemonkey/bugzilla/
// @description embeds bug history in comment flow and other tweaks
// @include */show_bug.cgi?*
// @require sitePrefs.js
// @require E4XtoDOM.js
// ==/UserScript==
// vim: set sw=2 : */
/**
* Bugzilla Flow - inspired by Bugzilla Tweaks
*
* This script includes multiple possible steps; each step is pushed into an
* array named "gSteps" and should be a function taking no arguments. The name
* of the function is used to determine if it should be run.
*/
const HTML_NS = 'http://www.w3.org/1999/xhtml';
var gSteps = [];
gSteps.push(function InlineActivity() {
// style things
GM_addStyle(""+<><![CDATA[
.bz_flow_entry {
font-size: 0.8em;
background-color: rgba(128, 128, 128, 0.15);
padding: 0.1em 3ch;
margin: 0 3ch;
}
.bz_flow_entry .removed, .bz_flow_entry .added {
font-family: monospace;
font-size: 1.1em;
}
.bz_flow_entry:first-of-type,
:not(.bz_flow_entry) + .bz_flow_entry {
border-top-left-radius: 1em;
border-top-right-radius: 1em;
padding-top: 0.5em;
}
.bz_flow_entry.last_in_series {
border-bottom-left-radius: 1em;
border-bottom-right-radius: 1em;
padding-bottom: 0.5em;
margin-bottom: 1em;
}
]]></>);
// create a hidden iframe to load history in... :|
var frame = document.createElement("iframe");
frame.style.display = "none";
frame.src = location.root + "show_activity.cgi?id=" + location.params.id;
frame.addEventListener("load", function() {
var entries = [];
var histRows = frame.contentDocument.querySelectorAll("table:not([id]) tr");
// The history table is... purely for visual purposes; it's semantically
// complete junk. We have to keep the first two columns around for when
// we hit a row without them
var lastWho = "<nobody>", lastWhen = new Date(0), lastWhenString = "";
Array.forEach(histRows, function(row) {
if (row.querySelector("th")) return; //heading row
let cells = Array.slice(row.getElementsByTagName("td"), 0);
cells = cells.map(function(c) c.textContent.replace(/\s+$/m,'').
replace(/^\s+/m,''));
if (cells.length == 5) {
lastWho = cells.shift();
lastWhenString = cells.shift();
lastWhen = Date.parse(lastWhenString.replace(/-/g, '/'));
}
if (cells.length != 3) {
console.warning("invalid number of cells in row: " + cells.length);
return;
}
entries.push({
when: lastWhen,
elem: E4XtoDOM(<>
<div class="bz_flow_entry" xmlns={HTML_NS}>
<span class="who">{lastWho}</span>
@
<span class="when">{lastWhenString}</span>
:
<span class="what">{cells[0]}</span>
<span class="removed">{cells[1]}</span>
<span class="added">{cells[2]}</span>
</div>
</>).firstChild
});
});
entries.sort(function(a, b) a.when - b.when);
// we now have reasonable history with which to play with
var lastComment = document.querySelector(".bz_comment:first-of-type").previousSibling;
Array.forEach(document.getElementsByClassName("bz_comment"), function(comment) {
if (entries.length < 1) {
return;
}
let timeElem = comment.querySelector(".bz_comment_time");
if (!timeElem) {
timeElem = comment.querySelector("th + td > b");
if (timeElem) timeElem = timeElem.nextSibling;
}
if (!timeElem) {
// old bugzilla
timeElem = comment.querySelector(".bz_comment_head a:last-of-type").
nextSibling;
}
let time = Date.parse(timeElem.textContent.
replace(/\s+$/m, '').
replace(/^\s+/m, '').
replace(/-/g, '/'));
let entry = null;
while (entries.length > 0 && time > entries[0].when) {
entry = entries.shift();
elem = comment.parentNode.insertBefore(entry.elem, comment);
}
if (entry) {
entry.elem.className += (" last_in_series");
}
lastComment = comment;
});
for each (let entry in entries) {
lastComment.parentNode.insertBefore(entry.elem, lastComment.nextSibling);
}
}, false);
document.body.appendChild(frame);
});
if(false)
gSteps.push(function Preferences() {
var prefsContainer = E4XtoDOM(<>
<form xmlns={HTML_NS} id="bz_flow_prefs">
<h3>Bugzilla Flow Preferences</h3>
</form>
</>).firstChild;
for each (let step in gSteps) {
if (step == Preferences) {
// never allow setting prefs on this step - we want to make sure we never
// end up disabling prefs setting!
continue;
}
var stepParams = E4XtoDOM(<>
<input xmlns={HTML_NS} type="checkbox"
id={"bz_flow_step_"+step.name}/>
<label xmlns={HTML_NS} for={"bz_flow_step_"+step.name}>{step.name}</label>
</>);
stepParams.querySelector('[id="bz_flow_step_' + step.name+'"]').
addEventListener("change", function(event) {
if (!event.target.checked && !event.target.indeterminate) {
event.target.indeterminate = true;
event.target.checked = true;
}
}, false);
prefsContainer.appendChild(stepParams);
}
document.body.appendChild(prefsContainer);
// style the prefs pseudo-dialog
GM_addStyle("" + <><![CDATA[
#bz_flow_prefs {
position: fixed;
top: 20px;
right: 20px;
min-width: 300px;
min-height: 200px;
border: 1px solid black;
background-color: cornsilk;
}
#bz_flow_prefs h3 {
text-align: center;
}
]]></>);
});
/**
* Entry point for the main script
*/
(function main(){ /* wrapper to make komodo syntax checking happy */
///// make sure this is a bugzilla page
if (!/^\?/(location.search)) return;
location.params = location.search.
substr(1).
split('&').
reduce(function(obj, val) {
var k = val.split('=', 1)[0];
obj[k] = val.substr(k.length + 1);
return obj;
}, {});
if (!("id/") in location.params) {
// no bug number
return;
}
if (!(document.body.classList.contains('bz_bug_' + location.params.id))) {
// not a bug
return;
}
///// store the bugzilla instance root
location.root = location.href.replace(/\/show_bug.cgi\?.*$/, '/');
///// run the steps
for each (let step in gSteps) {
if (!getPref("step_" + step.name, true)) {
continue;
}
step();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment