Created
November 4, 2010 07:39
-
-
Save mook/662211 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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