// ==UserScript==
// @name           FogBugz Butler
// @namespace      http://www.arantius.com/misc/greasemonkey/
// @description    Clean up FogBugz to be not quite so stupid
// @include        http://fogbugz.root.net/fogbugz/default.php*
// ==/UserScript==

//
// This script will make fogbugz make a bit more sense:
//
// - Rearrange edit mode, to put the title, project, and area, in with all of
//   the other edit fields.
// - Remove the picture of the day.  I'm a coder, not an art gallery curator.
// - Remove the separate headers, to put the list view back into one table
//   when sorting by priority.
// - Nail down text areas so they don't shape shift.
// - Subscribe to all cases that I have participated in.  I need to know when
//   they change to do my job, dammit!
//      (You'll need to put your name in, to make it work for you.)
// - Replace link to "download" screenshots with a way to just farking VIEW it!
//   I don't need to save that crap to disk, with the multiple clicks that takes
//   just to click a few more times to lanch it in a viewer, and a few more
//   times to delete it.
//

function findParent(el, tagName) {
	var table=el;
	while (el && el.parentNode) {
		if (tagName==el.tagName) return el;
		el=el.parentNode;
	}
	return false;
}

function importNode(xmlObj,doc) {
    var me=arguments.callee;
    var xhtmlObj=<testing xmlns={me.Const.xhtmlns} />;
    if(!me.Static.parser) me.Static.parser=new me.Const.parseObject;
    xhtmlObj.test=xmlObj;
    var domTree=me.Static.parser[me.Const.parseFunction](xhtmlObj.toXMLString(),me.Const.mimeType);
    var importMe=domTree.documentElement.firstChild;
    while(importMe && importMe.nodeType!=1) importMe=importMe.nextSibling;
    return (importMe)?doc.importNode(importMe,true):null;
}
importNode.Const={
    'xhtmlns' : "http://www.w3.org/1999/xhtml",
    'mimeType' : "text/xml",
    'parseObject' : DOMParser,
    'parseFunction' : 'parseFromString'
};
importNode.Static={};

function moveBitIn(id, label, el) {
	var tr=<tr>
		<td valign="top" class="middleLeft">
			<span class="bugPrompt">
				<label for="{id}">{label}:</label>
			</span>
		</td>
		<td valign="top" class="middleRight">
		</td>
	</tr>;
	tr=importNode(tr, document);
	var newEl=el.cloneNode(true);
	newEl.style.width='300px';
	if ('undefined'!=typeof el.selectedIndex) {
		newEl.selectedIndex=el.selectedIndex;
	}
	el.parentNode.removeChild(el);
	tr.getElementsByTagName('td')[1].appendChild(newEl);
	insertBefore.parentNode.insertBefore(tr, insertBefore);
}

if (document.getElementById('ixPersonAssignedTo')) {
	//in edit mode
	
	// find the table the title / project / area is in
	var tableA=findParent( document.getElementById('idBugTitleEdit'), 'TABLE' );
	var rowsA=tableA.getElementsByTagName('tr');
	var tableB=findParent( document.getElementById('ixPersonAssignedTo'), 'TABLE' );
	var rowsB=tableB.getElementsByTagName('tr');
	// move the odd bits inside the table
	var insertBefore=rowsB[3];

	// title
	moveBitIn('idBugTitleEdit', 'Title', document.getElementById('idBugTitleEdit'));
	document.getElementById('idBugTitleEdit').style.width='296px';

	// project
	moveBitIn('ixProject', 'Project', 
		rowsA[2].getElementsByTagName('td')[0].getElementsByTagName('select')[0]
	);

	// area
	moveBitIn('ixArea', 'Area', 
		rowsA[2].getElementsByTagName('td')[1].getElementsByTagName('select')[0]
	);

	// remove the now empty table
	tableA.parentNode.removeChild(tableA);
}

if (document.getElementById('ixStatus')) {
	//in resolve mode
	
	// find the table the title / project / area is in
	var tableA=findParent( document.getElementById('idBugTitleEdit'), 'TABLE' );
	var rowsA=tableA.getElementsByTagName('tr');
	var tableB=findParent( document.getElementById('ixStatus'), 'TABLE' );
	var rowsB=tableB.getElementsByTagName('tr');
	// move the odd bits inside the table
	var insertBefore=rowsB[2];

	// title
	moveBitIn('idBugTitleEdit', 'Title', document.getElementById('idBugTitleEdit'));
	document.getElementById('idBugTitleEdit').style.width='296px';

	// fix up class names
	rowsB[2].getElementsByTagName('td')[0].className='topLeft';
	rowsB[2].getElementsByTagName('td')[1].className='topRight';
	rowsB[3].getElementsByTagName('td')[0].className='middleLeft';
	rowsB[3].getElementsByTagName('td')[1].className='middleRight';
}

// get rid of the "picture of the day"
var res = document.evaluate('//table[@class="miniReport"]//img[@alt="*"]',
	document, null,	XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); 
var i, el;
for (i=0; el=res.snapshotItem(i); i++) {
	var div=findParent(el, 'DIV');
	div.parentNode.removeChild(div);
}

// lose separate priority headings in grid view (I just one list, dammit!)
var first=true;
var h2s=document.getElementsByTagName('h2');
for (var i=0, h2; h2=h2s[i]; i++) {
	if (h2.textContent.match(/^Priority/)) {
		var row=findParent(h2, 'TR');
		if (!first) {
			row.parentNode.removeChild(row.nextSibling.nextSibling);
		}
		row.parentNode.removeChild(row);
		i--;
		first=false;
	}
}

// get rid of those crazy shape-changing textareas
var tas=document.getElementsByTagName('textarea');
for (var i=0, ta; ta=tas[i]; i++) {
	ta.removeAttribute('onfocus');
	ta.removeAttribute('onkeyup');
	ta.removeAttribute('onblur');
	//ta.removeAttribute('onkeypress');
	//ta.removeAttribute('onkeydown');
	//ta.removeAttribute('onclick');
}

// remove crappy help thing
unsafeWindow['showHelp']=function(){};

// subscribe to all my bugs.  I can't BELIEVE that this isn't default behavior
// first see if I did something in this bug
var doSubscribe=false;
if (!document.location.href.match('Unsubscribe')) {
	for (var i=0, a=null; a=document.links[i]; i++) {
		if ('Anthony Lieuallen'==a.textContent) doSubscribe=true;
		if (doSubscribe && 'Subscribe'==a.textContent) {
			// "click" the link
			document.location.assign(a.href);
		}
	}
}

/*
unsafeWindow.shrinkImagesToFit =function() {
	var rgImages = document.getElementsByTagName("img");
	var i;
	for (i=0; i<rgImages.length; i++) {
		if (rgImages[i].getAttribute("inline") != null) {
			rgImages[i].xwidth = rgImages[i].width;
			rgImages[i].xheight = rgImages[i].height;
			var bWidthChanged = false;
			if (rgImages[i].width > 720) {
				bWidthChanged = true;
				rgImages[i].width = 720;
			}
//			if (rgImages[i].height > 250) {
//				if (bWidthChanged) {
//					rgImages[i].width = rgImages[i].xwidth / (rgImages[i].xheight / 250);
//				}
//				rgImages[i].height = 250;
//			}
		}
	}
}
*/

// i don't want to download screenshots! just link to them
var a, imgs=document.evaluate(
	"//img[@inline='yes']",
	document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
);
for (var i=0, img=null; img=imgs.snapshotItem(i); i++) {
	a=img.parentNode;
	a.href='javascript:void(0);';
	a.addEventListener('click', function() {
		var img=this.getElementsByTagName('img')[0];

		var o=window.open();
		o.document.write("<img src='"+img.src+"' />");
		o.document.close();
	}, true);
}
