User:Tyrok1/monobook.js: Difference between revisions

From Rosetta Code
Content added Content deleted
(Attempting to force columns to contain a minimum of three primary language sections.)
(Testing the per-code example buttonbar)
Line 1: Line 1:
/*========================================
/*========================================
This comparison script was written by
This comparison script was written by
Tyrok1, and has been tested on IE 5.5,
Tyrok1, and has been tested on IE 7,
IE 6, IE 7, IE 8, Fx 3.6.2, and
IE 8, Fx 3.6.3, and Fe 5.0.380.
Fe 4.0.280.
========================================*/
========================================*/

function GetElWidth(el)
//define the relationship between the CSS classes and the language names
var buttonBarCodepadLanguages = [
{ cssClass: "c", padName: "C" },
{ cssClass: "cpp", padName: "C++" },
{ cssClass: "d", padName: "D" },
{ cssClass: "haskell", padName: "Haskell" },
{ cssClass: "lua", padName: "Lua" },
{ cssClass: "ocaml", padName: "OCaml" },
{ cssClass: "php", padName: "PHP" },
{ cssClass: "perl", padName: "Perl" },
{ cssClass: "python", padName: "Python" },
{ cssClass: "ruby", padName: "Ruby" },
{ cssClass: "scheme", padName: "Scheme" },
{ cssClass: "tcl", padName: "Tcl" }
];

function AddHandler(el, handlerType, func)
{
{
//add an event handler in a more cross-browser way
return (el.clientWidth ? el.clientWidth : el.offsetWidth);
if(el.addEventListener)
{
el.addEventListener(handlerType, func, false);
}
else if(el.attachEvent)
{
el.attachEvent("on" + handlerType, func);
}
else
{
eval("el.on" + handlerType + " = func;");
}
}
}


function ToCMulticolumn()
function IsChild(parent, child)
{
{
//check to see if an element is a child of another element
var gutter = 10;
while(child && child != parent)
//split the ToC into multiple columns
var tocEl = document.getElementById("toc");
var ulElsNodeList = tocEl.getElementsByTagName("ul");
if(tocEl && ulElsNodeList.length > 0)
{
{
child = child.parentNode;
//limit what we're looking at to only what's directly in the main list, and move from NodeList objects to Arrays
}
var ulEls = [ulElsNodeList[0]];
return (child == parent);
for(var u = 1; u < ulElsNodeList.length; ++u)
}

function ButtonBarSelectAll(preEl)
{
//select all code within a given <pre> element
var range = null;
var menuEl = preEl.getElementsByTagName("ul")[0];
if(((document.createRange && (range = document.createRange())) || (document.selection.createRange && (range = document.selection.createRange()))) && range.selectNodeContents)
{
//for Fx, Fe, and other more standards-compliant browsers
range.selectNodeContents(preEl);
range.setEndBefore(menuEl);
var sel = window.getSelection();
if(sel.setBaseAndExtent)
{
{
//odd variant of a few of the WebKit browsers
if(ulElsNodeList[u].parentNode == ulElsNodeList[0].parentNode)
sel.setBaseAndExtent(preEl, 0, menuEl, 0);
{
ulEls[ulEls.length] = ulElsNodeList[u];
}
}
}
else
var liElsNodeList = tocEl.getElementsByTagName("li");
var liEls = new Array();
for(var l = 0; l < liElsNodeList.length; ++l)
{
{
sel.removeAllRanges();
for(u = 0; u < ulEls.length; ++u)
sel.addRange(range);
{
if(liElsNodeList[l].parentNode == ulEls[u])
{
liEls[liEls.length] = liElsNodeList[l];
break;
}
}
}
}
}
else
{
//for IE
range = document.body.createTextRange();
range.moveToElementText(preEl);
var menuRange = document.body.createTextRange();
menuRange.moveToElementText(menuEl);
//deselect the popup menu
//show all of the <ul> tags so we can get their items' ideal widths
while(range.compareEndPoints("EndToStart", menuRange) > 0)
ulEls[0].oldDisplay = (ulEls[0].style.display && ulEls[0].style.display == "none" ? "none" : "inline-block");
for(u = 0; u < ulEls.length; ++u)
{
{
range.moveEnd("word", -1);
ulEls[u].style.display = "inline-block";
}
}
range.select();
}
//find the widest item and use that to set up an ideal number of columns
var maxWidth = 0;
//return the range so other functions can use it
for(l = 0; l < liEls.length; ++l)
return range;
}

function ButtonBarCreateSelectAll(preEl)
{
return function() {
//select all of the text in the pre element
ButtonBarSelectAll(preEl);
};
}

function ButtonBarCreateCopy(preEl)
{
return function() {
//copy all of the text in the pre element
//IE-only at this point
var range = ButtonBarSelectAll(preEl);
range.execCommand("Copy");
};
}

function ButtonBarGetText(preEl)
{
//get the code from inside of a <pre> element
var outText = "";
for(var c = 0; c < preEl.childNodes.length; ++c)
{
switch(preEl.childNodes[c].tagName ? preEl.childNodes[c].tagName.toLowerCase() : "")
{
{
case "br":
if(!liEls[l].initialWidth)
{
{
outText += "\n";
liEls[l].initialWidth = GetElWidth(liEls[l]);
}
} break;
case "ul":
maxWidth = Math.max(liEls[l].initialWidth, maxWidth);
if(navigator.appVersion.indexOf("MSIE 5.") >= 0 || navigator.appVersion.indexOf("MSIE 6.") >= 0 || navigator.appVersion.indexOf("MSIE 7.") >= 0)
{
{
//get rid of &nbsp;s
liEls[l].style.display = "inline";
outText = outText.replace(/\xA0/g, " ");
//return the cleaned text
return outText;
} break;
default:
{
outText += (preEl.childNodes[c].textContent ? preEl.childNodes[c].textContent : (preEl.childNodes[c].innerText ? preEl.childNodes[c].innerText : preEl.childNodes[c].data));
}
}
}
}
}
//add in the gutter
//get rid of &nbsp;s
maxWidth += gutter;
outText = outText.replace(/\xA0/g, " ");
//figure out the ideal number of columns
//return the cleaned text
tocEl.style.width = "100%";
return outText;
var idealColumns = Math.min(Math.floor(GetElWidth(tocEl) / maxWidth), Math.floor(liEls.length / 3) + 1);
}
if(navigator.appVersion.indexOf("MSIE 5.") >= 0 || navigator.appVersion.indexOf("MSIE 6.") >= 0 || navigator.appVersion.indexOf("MSIE 7.") >= 0)

{
function ButtonBarAddSpacer(ulEl)
idealColumns = 1;
{
}
//add a spacer bullet item to the menu list
var spacerEl = ulEl.appendChild(document.createElement("li"));
//see if we've already got the ideal number of columns
spacerEl.appendChild(document.createTextNode("\u2022"));
//if so, we don't need to change anything
spacerEl.style.display = "inline";
if(ulEls.length != idealColumns)
spacerEl.style.marginLeft = "0.5em";
{
spacerEl.style.marginRight = "0.5em";
//split the list into multiple columns of width (95 / idealColumns)%
}
//first, make sure we have enough lists to split into

while(ulEls.length < idealColumns)
function ButtonBarCreateMouseOver(preEl)
{
//the mouseover event handler for <pre> elements
return function() {
//if there's already a menu, don't add another
if(preEl.getElementsByTagName("ul").length > 0)
{
return false;
}
//style the <pre> and add the menu
preEl.style.position = "relative";
var ulEl = preEl.appendChild(document.createElement("ul"));
ulEl.className = "ButtonBar";
ulEl.style.position = "absolute";
ulEl.style.right = 0;
ulEl.style.top = 0;
ulEl.style.whitespace = "nowrap";
ulEl.style.marginRight = "0.5em";
//define actions we want to add
var listItems = [
{title:"Select All",code:ButtonBarCreateSelectAll(preEl)}
];
if(window.clipboardData)
{
listItems[listItems.length] = {title:"Copy to Clipboard",code:ButtonBarCreateCopy(preEl)};
}
//add the standard menu items
for(var i = 0; i < listItems.length; ++i)
{
//if it's not the first one, add a spacer
if(i > 0)
{
ButtonBarAddSpacer(ulEl);
}
var liEl = ulEl.appendChild(document.createElement("li"));
liEl.appendChild(document.createTextNode(listItems[i].title));
liEl.style.cursor = "pointer";
liEl.style.display = "inline";
liEl.style.fontFamily = "sans-serif";
liEl.style.textDecoration = "underline";
AddHandler(liEl, "click", listItems[i].code);
}
//check for a codepad-supported language
var langClass = preEl.className.split(" ")[0];
for(var l = 0; l < buttonBarCodepadLanguages.length; ++l)
{
if(langClass == buttonBarCodepadLanguages[l].cssClass)
{
//found the language
//add <li>s
ButtonBarAddSpacer(ulEl);
var liEl = ulEl.appendChild(document.createElement("li"));
liEl.style.display = "inline";
//build a form to submit to codepad
var formEl = liEl.appendChild(document.createElement("form"));
formEl.target = "_blank";
formEl.method = "post";
formEl.action = "http://codepad.org";
formEl.style.display = "inline";
var inputValues = [
{ name: "lang", value: buttonBarCodepadLanguages[l].padName },
{ name: "code", value: ButtonBarGetText(preEl) },
{ name: "run", value: "True" },
{ name: "private", value: "True" }
];
for(var i = 0; i < inputValues.length; ++i)
{
var inputEl = document.createElement("input");
inputEl.setAttribute("type", "hidden");
inputEl.setAttribute("name", inputValues[i].name);
inputEl.setAttribute("value", inputValues[i].value);
formEl.appendChild(inputEl);
}
//add and style the submit button to look like the other items
var inputEl = document.createElement("input");
inputEl.setAttribute("type", "submit");
inputEl.setAttribute("name", "submit");
inputEl.setAttribute("value", "Try on Codepad");
inputEl.style.border = "none";
inputEl.style.background = "transparent";
inputEl.style.cursor = "pointer";
inputEl.style.textDecoration = "underline";
inputEl.style.display = "inline-block";
inputEl.style.padding = 0;
formEl.appendChild(inputEl);
break;
}
}
return false;
};
}

function ButtonBarCreateMouseOut(preEl)
{
//mouseout handler
return function(e) {
//if we're moving to another element inside of the same <pre>, add a mouseout handler and return
if(!e) e = window.event;
var target = e.relatedTarget || e.toElement;
if(IsChild(preEl, target))
{
{
AddHandler(target, "mouseout", ButtonBarCreateMouseOut);
ulEls[ulEls.length] = ulEls[ulEls.length - 1].parentNode.insertBefore(document.createElement("ul"), ulEls[ulEls.length - 1].nextSibling);
return false;
}
}
//we're moving away from the container <pre>, so we need to remove all menu lists
//style the lists
var ulEls = preEl.getElementsByTagName("ul");
for(var c = 0; c < ulEls.length; ++c)
for(var u = 0; u < ulEls.length; ++u)
{
{
ulEls[c].style.display = "inline-block";
ulEls[u].parentNode.removeChild(ulEls[u]);
ulEls[c].style.cssFloat = "left";
ulEls[c].style.verticalAlign = "top";
ulEls[c].style.width = (95 / idealColumns) + "%";
}
}
return false;
};
//sort the items into the lists
for(l = 0; l < liEls.length; ++l)
{
liEls[l] = ulEls[Math.floor(l / liEls.length * idealColumns)].appendChild(liEls[l].parentNode.removeChild(liEls[l]));
}
//get rid of any unnecessary lists
for(l = idealColumns; l < ulEls.length; ++l)
{
ulEls[l].parentNode.removeChild(ulEls[l]);
}
}
//hide the instructions/lists if they're supposed to be hidden
var insEl = document.getElementById("tocinstructions");
if(insEl)
{
insEl.style.display = (ulEls[0].oldDisplay.toLowerCase() == "none" ? "none" : "block");
}
for(u = 0; u < ulEls.length; ++u)
{
ulEls[u].style.display = ulEls[0].oldDisplay;
}
//if we're setting this up for the first time...
if(!window.toggleTocOld)
{
//change out the toggling function so it toggles all of the lists
window.toggleTocOld = window.toggleToc;
window.toggleUpdateVisibility = function() {
var tocEl = document.getElementById("toc");
var ulEls = tocEl.getElementsByTagName("ul");
for(var u = 0; u < ulEls.length; ++u)
{
ulEls[u].style.display = (ulEls[0].style.display && ulEls[0].style.display == "none" ? "none" : "inline-block");
}
var el = document.getElementById("tocinstructions");
if(el)
{
el.style.display = (ulEls[0].style.display && ulEls[0].style.display.toLowerCase() == "none" ? "none" : "block");
}
};
window.toggleToc = function() {
toggleTocOld();
toggleUpdateVisibility();
};
//add an event handler so if the window's resized the number of columns can change
if(window.addEventListener)
{
window.addEventListener("resize", ToCMulticolumn, false);
}
else if(document.addEventListener)
{
document.addEventListener("resize", ToCMulticolumn, false);
}
else if(window.attachEvent)
{
window.attachEvent("onresize", ToCMulticolumn);
}
}
}
}
}


function CompareActivate()
function ButtonBarActivate()
{
{
//check to see if we're looking at a task page
//check to see if we're looking at a task page
Line 182: Line 296:
}
}
//look for language examples
//check to make sure we have a table of contents
var tocEl = document.getElementById("toc");
var allPres = document.getElementsByTagName("pre");
for(var p = 0; p < allPres.length; ++p)
if(tocEl)
{
{
if(allPres[p].className && allPres[p].className.indexOf("highlighted_source") >= 0)
//remove the extra show/hide button from the Contents area
var tocTitleEl = document.getElementById("toctitle");
if(tocTitleEl)
{
{
//set up the onmouseover, onmousleave events to show/hide the button bar
var spanEls = tocTitleEl.getElementsByTagName("span");
AddHandler(allPres[p], "mouseover", ButtonBarCreateMouseOver(allPres[p]));
var foundToggle = false;
AddHandler(allPres[p], "mouseout", ButtonBarCreateMouseOut(allPres[p]));
for(var s = 0; s < spanEls.length; ++s)
{
if(spanEls[s].className && spanEls[s].className.toLowerCase() == "toctoggle")
{
if(foundToggle)
{
spanEls[s].parentNode.removeChild(spanEls[s]);
}
foundToggle = true;
}
}
}
//find all of the links to language sections and add checkboxes
var liEls = tocEl.getElementsByTagName("li");
for(var l = 0; l < liEls.length; ++l)
{
//add a checkbox with the language section's <a> name attribute as its value for easier lookup in the refresh function
var checkboxEl = document.createElement("input");
checkboxEl.setAttribute("type", "checkbox");
checkboxEl.setAttribute("name", "CompareLanguage" + l);
checkboxEl.setAttribute("id", "CompareLanguage" + l);
var href = liEls[l].getElementsByTagName("a")[0].getAttribute("href");
checkboxEl.setAttribute("value", href.substring(href.indexOf("#") + 1, href.length));
checkboxEl.onclick = CompareRefresh;
liEls[l].insertBefore(checkboxEl, liEls[l].firstChild);
}
//add some instructions
var toctitleEl = document.getElementById("toctitle");
var instructionsEl = toctitleEl.appendChild(document.createElement("p"));
instructionsEl.setAttribute("id", "tocinstructions");
instructionsEl.appendChild(document.createTextNode("Check the boxes next to languages to compare them."));
instructionsEl.appendChild(document.createElement("br"));
instructionsEl.appendChild(document.createTextNode("Uncheck all boxes to show all languages."));
//switch the ToC to multi-column mode
ToCMulticolumn();
//put all language sections inside of a <div>
var aEls = document.getElementsByTagName("a");
var languages = CompareGetLanguages();
var divEl = null;
for(var a = 0; a < aEls.length; ++a)
{
if(aEls[a].name && typeof languages[aEls[a].name] != "undefined")
{
//we can assume that this is a language section
//make a list of all of the elements between this <a> tag and the next one that has a name attribute
divEl = aEls[a].parentNode.insertBefore(document.createElement("div"), aEls[a]);
var curEl = aEls[a].nextSibling;
divEl.appendChild(aEls[a].parentNode.removeChild(aEls[a]));
while(curEl && (!curEl.tagName || curEl.tagName.toLowerCase() != "a" || !curEl.name || typeof languages[curEl.name] == "undefined"))
{
var newCurEl = curEl.nextSibling;
divEl.appendChild(curEl.parentNode.removeChild(curEl));
curEl = newCurEl;
};
}
}
if(divEl)
{
//add a float clearing div
var clearEl = divEl.parentNode.insertBefore(document.createElement("div"), divEl.nextSibling);
clearEl.style.clear = "both";
}
//and refresh the display for good measure (sometimes browsers like to save <input> states between reloads)
CompareRefresh();
}
}

function CompareGetLanguages()
{
var tocEl = document.getElementById("toc");
var languages = new Array();
if(tocEl)
{
//find all of the checkboxes within the ToC
var inputEls = tocEl.getElementsByTagName("input");
for(var i = 0; i < inputEls.length; ++i)
{
//if it's one of the ones we added earlier, log the checked status to the languages array for later use
if(inputEls[i].getAttribute("name").substring(0, ("CompareLanguage").length) == "CompareLanguage")
{
languages[inputEls[i].value] = inputEls[i].checked;
}
}
}
return languages;
}

function CompareRefresh()
{
//refresh the display of the language sections displayed under the ToC
//first, check to see if we're looking at a task page
var codeMargin = 20;
var tocEl = document.getElementById("toc");
var languages = new Array();
var numChecked = 0;
if(tocEl)
{
//count how many languages are checked
languages = CompareGetLanguages();
for(var l in languages)
{
if(languages[l])
{
++numChecked;
}
}
//now, find all <a> tags with name attributes
var aEls = document.getElementsByTagName("a");
for(var a = 0; a < aEls.length; ++a)
{
if(aEls[a].name && typeof languages[aEls[a].name] != "undefined")
{
//we can assume that this is a language section
//check to see if we're displaying in side-by-side mode, regular mode, or not at all
if(numChecked < 1 || (languages[aEls[a].name] && (navigator.appVersion.indexOf("MSIE 5.") >= 0 || navigator.appVersion.indexOf("MSIE 6.") >= 0 || navigator.appVersion.indexOf("MSIE 7.") >= 0)))
{
//enable full-width mode
aEls[a].parentNode.style.display = "block";
aEls[a].parentNode.style.cssFloat = "none";
aEls[a].parentNode.style.width = "auto";
aEls[a].parentNode.style.marginRight = 0;
}
else if(languages[aEls[a].name])
{
//enable side-by-side mode
aEls[a].parentNode.style.display = "inline-block";
aEls[a].parentNode.style.verticalAlign = "top";
aEls[a].parentNode.style.marginRight = "20px";
//find the width for this language
var preEls = aEls[a].parentNode.getElementsByTagName("pre");
var maxWidth = 0;
for(var p = 0; p < preEls.length; ++p)
{
var oldWidth = GetElWidth(preEls[p]);
preEls[p].style.cssFloat = "left";
if(GetElWidth(preEls[p]) == oldWidth)
{
//assume it's either wider than the window or IE where they don't shrink
//arbitrarily set to 45% width
maxWidth = Math.max(GetElWidth(aEls[a].parentNode.parentNode) * 0.45, maxWidth);
}
else
{
maxWidth = Math.max(GetElWidth(preEls[p]), maxWidth);
}
preEls[p].style.cssFloat = "none";
}
//if there are no code blocks, base it off of the <h2>
if(!maxWidth)
{
var h2Els = aEls[a].parentNode.getElementsByTagName("h2");
if(h2Els.length > 0)
{
//we have at least one h2
//use its width
var oldWidth = GetElWidth(h2Els[p]);
h2Els[0].style.cssFloat = "left";
if(GetElWidth(h2Els[p]) == oldWidth)
{
//assume it's either wider than the window or IE where they don't shrink
//arbitrarily set to 45% width
maxWidth = Math.max(GetElWidth(aEls[a].parentNode.parentNode) * 0.45, maxWidth);
}
else
{
maxWidth = GetElWidth(h2Els[0]);
}
h2Els[0].style.cssFloat = "none";
}
else
{
//no clue what it should be
//arbitrarily set it to 200
maxWidth = 200;
}
}
//set the container <div> to a size just a little bit larger
aEls[a].parentNode.style.width = (maxWidth + codeMargin) + "px";
}
else
{
//hide it entirely
aEls[a].parentNode.style.display = "none";
}
}
}
}
}
}
Line 391: Line 310:


//register the comparison script with the window's load event
//register the comparison script with the window's load event
AddHandler(window, "load", ButtonBarActivate);
if(window.addEventListener)
{
window.addEventListener("load", CompareActivate, false);
}
else if(window.attachEvent)
{
window.attachEvent("onload", CompareActivate);
}
else if(document.addEventListener)
{
document.addEventListener("load", CompareActivate, false);
}
else
{
window.onload = CompareActivate;
}

Revision as of 01:06, 6 July 2010

/*========================================
This comparison script was written by
Tyrok1, and has been tested on IE 7,
IE 8, Fx 3.6.3, and Fe 5.0.380.
========================================*/

//define the relationship between the CSS classes and the language names
var buttonBarCodepadLanguages = [
					{ cssClass: "c", padName: "C" },
					{ cssClass: "cpp", padName: "C++" },
					{ cssClass: "d", padName: "D" },
					{ cssClass: "haskell", padName: "Haskell" },
					{ cssClass: "lua", padName: "Lua" },
					{ cssClass: "ocaml", padName: "OCaml" },
					{ cssClass: "php", padName: "PHP" },
					{ cssClass: "perl", padName: "Perl" },
					{ cssClass: "python", padName: "Python" },
					{ cssClass: "ruby", padName: "Ruby" },
					{ cssClass: "scheme", padName: "Scheme" },
					{ cssClass: "tcl", padName: "Tcl" }
				];

function AddHandler(el, handlerType, func)
{
	//add an event handler in a more cross-browser way
	if(el.addEventListener)
	{
		el.addEventListener(handlerType, func, false);
	}
	else if(el.attachEvent)
	{
		el.attachEvent("on" + handlerType, func);
	}
	else
	{
		eval("el.on" + handlerType + " = func;");
	}
}

function IsChild(parent, child)
{
	//check to see if an element is a child of another element
	while(child && child != parent)
	{
		child = child.parentNode;
	}
	return (child == parent);
}

function ButtonBarSelectAll(preEl)
{
	//select all code within a given <pre> element
	var range = null;
	var menuEl = preEl.getElementsByTagName("ul")[0];
	if(((document.createRange && (range = document.createRange())) || (document.selection.createRange && (range = document.selection.createRange()))) && range.selectNodeContents)
	{
		//for Fx, Fe, and other more standards-compliant browsers
		range.selectNodeContents(preEl);
		range.setEndBefore(menuEl);
		var sel = window.getSelection();
		if(sel.setBaseAndExtent)
		{
			//odd variant of a few of the WebKit browsers
			sel.setBaseAndExtent(preEl, 0, menuEl, 0);
		}
		else
		{
			sel.removeAllRanges();
			sel.addRange(range);
		}
	}
	else
	{
		//for IE
		range = document.body.createTextRange();
		range.moveToElementText(preEl);
		var menuRange = document.body.createTextRange();
		menuRange.moveToElementText(menuEl);
		
		//deselect the popup menu
		while(range.compareEndPoints("EndToStart", menuRange) > 0)
		{
			range.moveEnd("word", -1);
		}
		range.select();
	}
	
	//return the range so other functions can use it
	return range;
}

function ButtonBarCreateSelectAll(preEl)
{
	return function() {
				//select all of the text in the pre element
				ButtonBarSelectAll(preEl);
			};
}

function ButtonBarCreateCopy(preEl)
{
	return function() {
				//copy all of the text in the pre element
				//IE-only at this point
				var range = ButtonBarSelectAll(preEl);
				range.execCommand("Copy");
			};
}

function ButtonBarGetText(preEl)
{
	//get the code from inside of a <pre> element
	var outText = "";
	for(var c = 0; c < preEl.childNodes.length; ++c)
	{
		switch(preEl.childNodes[c].tagName ? preEl.childNodes[c].tagName.toLowerCase() : "")
		{
			case "br":
			{
				outText += "\n";
			} break;
			case "ul":
			{
				//get rid of &nbsp;s
				outText = outText.replace(/\xA0/g, " ");
				
				//return the cleaned text
				return outText;
			} break;
			default:
			{
				outText += (preEl.childNodes[c].textContent ? preEl.childNodes[c].textContent : (preEl.childNodes[c].innerText ? preEl.childNodes[c].innerText : preEl.childNodes[c].data));
			}
		}
	}
	
	//get rid of &nbsp;s
	outText = outText.replace(/\xA0/g, " ");
	
	//return the cleaned text
	return outText;
}

function ButtonBarAddSpacer(ulEl)
{
	//add a spacer bullet item to the menu list
	var spacerEl = ulEl.appendChild(document.createElement("li"));
	spacerEl.appendChild(document.createTextNode("\u2022"));
	spacerEl.style.display = "inline";
	spacerEl.style.marginLeft = "0.5em";
	spacerEl.style.marginRight = "0.5em";
}

function ButtonBarCreateMouseOver(preEl)
{
	//the mouseover event handler for <pre> elements
	return function() {
				//if there's already a menu, don't add another
				if(preEl.getElementsByTagName("ul").length > 0)
				{
					return false;
				}
				
				//style the <pre> and add the menu
				preEl.style.position = "relative";
				var ulEl = preEl.appendChild(document.createElement("ul"));
				ulEl.className = "ButtonBar";
				ulEl.style.position = "absolute";
				ulEl.style.right = 0;
				ulEl.style.top = 0;
				ulEl.style.whitespace = "nowrap";
				ulEl.style.marginRight = "0.5em";
				
				//define actions we want to add
				var listItems = [
								{title:"Select All",code:ButtonBarCreateSelectAll(preEl)}
							];
				if(window.clipboardData)
				{
					listItems[listItems.length] = {title:"Copy to Clipboard",code:ButtonBarCreateCopy(preEl)};
				}
				
				//add the standard menu items
				for(var i = 0; i < listItems.length; ++i)
				{
					//if it's not the first one, add a spacer
					if(i > 0)
					{
						ButtonBarAddSpacer(ulEl);
					}
					var liEl = ulEl.appendChild(document.createElement("li"));
					liEl.appendChild(document.createTextNode(listItems[i].title));
					liEl.style.cursor = "pointer";
					liEl.style.display = "inline";
					liEl.style.fontFamily = "sans-serif";
					liEl.style.textDecoration = "underline";
					AddHandler(liEl, "click", listItems[i].code);
				}
				
				//check for a codepad-supported language
				var langClass = preEl.className.split(" ")[0];
				for(var l = 0; l < buttonBarCodepadLanguages.length; ++l)
				{
					if(langClass == buttonBarCodepadLanguages[l].cssClass)
					{
						//found the language
						//add <li>s
						ButtonBarAddSpacer(ulEl);
						var liEl = ulEl.appendChild(document.createElement("li"));
						liEl.style.display = "inline";
						
						//build a form to submit to codepad
						var formEl = liEl.appendChild(document.createElement("form"));
						formEl.target = "_blank";
						formEl.method = "post";
						formEl.action = "http://codepad.org";
						formEl.style.display = "inline";
						var inputValues = [
									{ name: "lang", value: buttonBarCodepadLanguages[l].padName },
									{ name: "code", value: ButtonBarGetText(preEl) },
									{ name: "run", value: "True" },
									{ name: "private", value: "True" }
								];
						for(var i = 0; i < inputValues.length; ++i)
						{
							var inputEl = document.createElement("input");
							inputEl.setAttribute("type", "hidden");
							inputEl.setAttribute("name", inputValues[i].name);
							inputEl.setAttribute("value", inputValues[i].value);
							formEl.appendChild(inputEl);
						}
						
						//add and style the submit button to look like the other items
						var inputEl = document.createElement("input");
						inputEl.setAttribute("type", "submit");
						inputEl.setAttribute("name", "submit");
						inputEl.setAttribute("value", "Try on Codepad");
						inputEl.style.border = "none";
						inputEl.style.background = "transparent";
						inputEl.style.cursor = "pointer";
						inputEl.style.textDecoration = "underline";
						inputEl.style.display = "inline-block";
						inputEl.style.padding = 0;
						formEl.appendChild(inputEl);
						break;
					}
				}
				return false;
			};
}

function ButtonBarCreateMouseOut(preEl)
{
	//mouseout handler
	return function(e) {
			//if we're moving to another element inside of the same <pre>, add a mouseout handler and return
			if(!e) e = window.event;
			var target = e.relatedTarget || e.toElement;
			if(IsChild(preEl, target))
			{
				AddHandler(target, "mouseout", ButtonBarCreateMouseOut);
				return false;
			}
			
			//we're moving away from the container <pre>, so we need to remove all menu lists
			var ulEls = preEl.getElementsByTagName("ul");
			for(var u = 0; u < ulEls.length; ++u)
			{
				ulEls[u].parentNode.removeChild(ulEls[u]);
			}
			return false;
		};
}

function ButtonBarActivate()
{
	//check to see if we're looking at a task page
	var catLinksEl = document.getElementById("catlinks");
	var isTask = false;
	if(catLinksEl)
	{
		var aEls = catLinksEl.getElementsByTagName("a");
		for(var a = 0; a < aEls.length; ++a)
		{
			if(aEls[a].getAttribute("title") == "Category:Programming Tasks")
			{
				//it's a task
				isTask = true;
				break;
			}
		}
	}
	if(!isTask)
	{
		return;
	}
	
	//look for language examples
	var allPres = document.getElementsByTagName("pre");
	for(var p = 0; p < allPres.length; ++p)
	{
		if(allPres[p].className && allPres[p].className.indexOf("highlighted_source") >= 0)
		{
			//set up the onmouseover, onmousleave events to show/hide the button bar
			AddHandler(allPres[p], "mouseover", ButtonBarCreateMouseOver(allPres[p]));
			AddHandler(allPres[p], "mouseout", ButtonBarCreateMouseOut(allPres[p]));
		}
	}
}

//register the comparison script with the window's load event
AddHandler(window, "load", ButtonBarActivate);