<!--	Hide script from older browsers
// code to try to make the rankle ranking/ordering widget work correctly.

// this is copyright Daniel Kahn Gillmor 2004, and is hereby released
// under the GPLv2.


function rankleError(txt) {
	alert(txt);
	throw txt;
}


function rankleCheckClass(obj,cl) {
	if ((obj == null) || 
	    (obj == undefined)) {
		rankleError("called CheckClass("+cl+") on a null or undefined object");
	}
	if (obj.className != cl) {
		rankleError("this should have been of class " + cl + "!\n" +
			"but it was class " + obj.className); return;
	}
}	



function rankleUnrankAll(rr) {
	var rr = rankleGetAncestorOfClass(rr, "rankle");

	if (rankleWhichView(rr) == "fancy") {
		var or = rankleGetOrderedListForWidget(rr);
		while (or.childNodes.length > 0) {
			rankleUnrank(or.firstChild);
		}
	} else {
		var items = rr.getElementsByTagName("div");

		for (var zz = 0; zz < items.length; zz++) {
			if (items.item(zz).className == "rankle-item") {
				var input = rankleGetInputForItem(items.item(zz));
				input.value = "";
			}
		}
	}
}

// returns either "fancy" or "plain" to describe which view mode the rankle widget is using:
function rankleWhichView(rr) {
	rr = rankleGetAncestorOfClass(rr, "rankle");

	//if either list is visible, we must be in fancy view:
	if (rankleGetOrderedListForWidget(rr).style.display == "none") {
		return "plain";
	} else {
		return "fancy";
	}
}

function rankleSwitchView(rr) {
	rr = rankleGetAncestorOfClass(rr, "rankle");

	if (rankleWhichView(rr) == "plain") {
		rankleConvertToFancy(rr);
	} else {
		rankleConvertToPlain(rr);
	}
}

function ranklePromote(ritem) {
	ritem = rankleGetAncestorOfTagName(ritem, "li");

	var parent = ritem.parentNode;
	var rr = rankleGetAncestorOfClass(parent, "rankle");
	
	var or = rankleGetOrderedListForWidget(rr);
	var un = rankleGetUnorderedListForWidget(rr);
	
	if (un == parent) {
		un.removeChild(ritem);
		or.appendChild(ritem);
		
	} else if (or == parent) {
		// what should we do if this is called on a ranked item?
		// we need to see who the prevSibling is.
		if (ritem.previousSibling != null) {
			var targ = ritem.previousSibling;
			or.removeChild(ritem);
			or.insertBefore(ritem,targ);
		}
	}
	rankleSyncInputVars(rr);
	return ritem;
}

function rankleUnrank(ritem) {
	ritem = rankleGetAncestorOfTagName(ritem, "li");

	var un = rankleGetUnorderedListForWidget(rankleGetAncestorOfClass(ritem, "rankle"));
	ritem.parentNode.removeChild(ritem);
	un.appendChild(ritem);
	rankleSyncInputVars(rankleGetAncestorOfClass(ritem, "rankle"));
}


function rankleTopRank(ritem) {
	ritem = rankleGetAncestorOfTagName(ritem, "li");

	var or = rankleGetOrderedListForWidget(rankleGetAncestorOfClass(ritem, "rankle"));
	ritem.parentNode.removeChild(ritem);
	if (or.hasChildNodes()) {
		or.insertBefore(ritem,or.firstChild);
	} else {
		or.appendChild(ritem);
	}
	rankleSyncInputVars(rankleGetAncestorOfClass(ritem, "rankle"));
	return ritem;
}


function rankleDemote(ritem) {
	ritem = rankleGetAncestorOfTagName(ritem, "li");

	var parent = ritem.parentNode;
	var rr = rankleGetAncestorOfClass(ritem, "rankle");
	rankleCheckClass(rr, "rankle");
	
	var or = rankleGetOrderedListForWidget(rr);
	var un = rankleGetUnorderedListForWidget(rr);
	
	if (or == parent) {
		if (ritem.nextSibling == null) {
			or.removeChild(ritem);
			un.appendChild(ritem);
		} else {
			var targ = ritem.nextSibling;
			or.removeChild(ritem);
			if (targ.nextSibling == null) {
				or.appendChild(ritem);
			} else {
				or.insertBefore(ritem, targ.nextSibling);
			}
		}
	} else if (un == parent) {
		// what should we do if this is called on an unranked item?
	}
	rankleSyncInputVars(rr);
}

// steps through the available rankings and updates the <input> elements to match.

function rankleSyncInputVars(rr) {
	rr = rankleGetAncestorOfClass(rr, "rankle");
	
	var or = rankleGetOrderedListForWidget(rr);
	var un = rankleGetUnorderedListForWidget(rr);

	for (var zz = 0; zz < or.childNodes.length; zz++) {
		var rli = or.childNodes.item(zz);
		var input = rankleGetInputForItem(rli);
		input.value = zz+1; 
		rli.value = zz+1;
		rankleSetupRankedItem(rli);
	}
	if (un.hasChildNodes()) {
		// un-hide it:
		un.style.display = "block";
		for (var zz = 0; zz < un.childNodes.length; zz++) {
			var input = rankleGetInputForItem(un.childNodes.item(zz));
			input.value = "";
			rankleSetupUnrankedItem(un.childNodes.item(zz));
		}
	} else {
		un.style.display = "none";
	}
}


function rankleNewRankLabel(rank) {
	var bb = document.createElement("span");
	bb.className = "rankle-rank-label";
	bb.appendChild(document.createTextNode(rank+". "));
	return bb;
}

function rankleNewIconButton(ch,script,tooltip, decoration) {
	var bb = document.createElement("a");
	bb.className = "rankle-icon-button";
	if (tooltip != undefined) {
		bb.title = tooltip;
	}
	if (decoration != undefined) {
		bb.style.textDecoration = decoration;
	}
	bb.appendChild(document.createTextNode(ch));
	bb.onclick = script;
	return bb;
}

function rankleSetupRankedItem(rli) {
//	var tt = rankleGetTitleSpanForItem(rli);
	var v = rankleGetLiValue(rli);
	rli.onclick = null;
	rli.onmousedown = rankleHandleOrderedClick;
	rli.title = "click to select";
	rankleRemoveButtonsForItem(rli);
	rankleRemoveRankLabelForItem(rli);
	rli.insertBefore(rankleNewRankLabel(v),rli.firstChild);
//	rli.appendChild(rankleNewPromoteButton());
//	rli.appendChild(rankleNewTopRankButton());
//	rli.appendChild(rankleNewDemoteButton());
	rli.appendChild(rankleNewUnrankButton());
}
function rankleSetupUnrankedItem(rli) {
//	var tt = rankleGetTitleSpanForItem(rli);
	rli.onmousedown = null;
	rli.onclick = rankleHandleUnorderedClick;
	rli.title = "click to promote";
	rankleRemoveButtonsForItem(rli);
	rankleRemoveRankLabelForItem(rli);
//	rli.appendChild(rankleNewPromoteButton());
	rli.appendChild(rankleNewTopRankButton());
	rli.className = null;
}

function rankleNewPromoteButton() {
	return rankleNewIconButton("\u21d1", function(){ranklePromote(this);}, "click to promote");
}
function rankleNewDemoteButton() {
	return rankleNewIconButton("\u21d3", function(){rankleDemote(this);}, "click to demote");
}

function rankleNewUnrankButton() {
	return rankleNewIconButton("\u21d3", rankleHandleUnrankButton, "click to unrank", "underline");
}
function rankleNewTopRankButton() {
	return rankleNewIconButton("\u21d1", rankleHandleTopRankButton, "click to rank top", "overline");
}





function rankleRemoveDescendentsWithTagClass(item,tagname,cname) {
	var spans = item.getElementsByTagName(tagname);
	var zz = 0;
	while (zz < spans.length) {
		if (spans.item(zz).className == cname) {
			item.removeChild(spans.item(zz));
		} else {
			zz++;
		}
	}
}
function rankleGetDescendentWithTagClass(item,tagname,cname, n) {
	if (n == undefined) {
		n = 0;
	}
	var targcount = 0;
	var spans = item.getElementsByTagName(tagname);
	for (var zz = 0; zz < spans.length; zz++) {
		if (spans.item(zz).className == cname) {
			if (targcount >= n) {
				return spans.item(zz);
			} else {
				targcount++;
			}
		} 
	}
	rankleError("couldn't find "+n+"th item with tag '" + tagname + "' and class '"	+cname+ "'.");
}

function rankleRemoveRankLabelForItem(rli) {
//	rankleCheckClass(rli, "rankle-li");
	return rankleRemoveDescendentsWithTagClass(rli, "span", "rankle-rank-label");
}	

function rankleRemoveButtonsForItem(rli) {
//	rankleCheckClass(rli, "rankle-li");
	return rankleRemoveDescendentsWithTagClass(rli, "a", "rankle-icon-button");
}

// generates a new button that can be placed some place, which calls
// the given code when it is clicked.  supplying a tooltip is optional.

function rankleNewControl(title, script, tooltip, name) {
	var cc = document.createElement("a");
	cc.className = "rankle-control";
	cc.onclick = script;
	cc.appendChild(document.createTextNode(title));
	if (tooltip != undefined) {
		cc.title = tooltip;
	}
	if (name != undefined) {
		cc.name = name;
	}
	return cc;
}

function rankleToggleSelectItem(clickee) {
	var ritem = rankleGetAncestorOfTagName(clickee, "li");
	
	if (!ritem.className) {
		ritem.className = "rankle-selected"; 
	} else {
		ritem.className = null;
	}
}


function ranklePromoteAllSelected(rr) {
	rr = rankleGetAncestorOfClass(rr, "rankle");
	var or = rankleGetOrderedListForWidget(rr);

	var lis = or.getElementsByTagName("li");

	for(var z=1; z < lis.length; z++) {
		if ((lis.item(z).className == "rankle-selected") && 
			(lis.item(z-1).className != "rankle-selected")) {
			ranklePromote(lis.item(z));
		}
	}
}

function rankleDemoteAllSelected(rr) {
	rr = rankleGetAncestorOfClass(rr, "rankle");
	var or = rankleGetOrderedListForWidget(rr);

	var lis = or.getElementsByTagName("li");

	for(var z=lis.length-1; z >= 0; z--) {
		if (lis.item(z).className == "rankle-selected") {
			rankleDemote(lis.item(z));
		}
	}
}

function rankleUnselectAll(rr) {
	rr = rankleGetAncestorOfClass(rr, "rankle");
	var lis = rr.getElementsByTagName("li");
	
	for(var z=0; z < lis.length; z++) {
		lis.item(z).className = null;
	}
}

function rankleHandleTopRankButton(e) {
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	var btn = this;
	var rli = rankleTopRank(btn);
	// and select it:
	if (!e.ctrlKey) { // NN4 might fail here.  it prefers e.modifiers (which is a bitfield)
		rankleUnselectAll(rli); 
	}
	rankleToggleSelectItem(rli);
	

}
function rankleHandleUnrankButton(e) {
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	var ret = rankleUnrank(this);
	return ret;
}

function rankleHandleOrderedClick(e) {
	if (!e) var e = window.event;
	if (!e.ctrlKey) { // NN4 might fail here.  it prefers e.modifiers (which is a bitfield)
		rankleUnselectAll(this); 
	}
	rankleToggleSelectItem(this);
}

function rankleHandleUnorderedClick(e) {
	if (!e) var e = window.event;

	var ret = ranklePromote(this);
	if (!e.ctrlKey) { // NN4 might fail here.  it prefers e.modifiers (which is a bitfield)
		rankleUnselectAll(ret); 
	}
	rankleToggleSelectItem(ret);
}	


// for the next full revision, we want to build the form completely 
function rankleSetup(rr) {
	rankleCheckClass(rr, "rankle");
	
	var lists = document.createElement("div");
	// remove all the rankle-items and put them in the lists div:

	var zz = 0;
	var children = rr.getElementsByTagName("div");
	while (zz < children.length) {
		if (children.item(zz).className == "rankle-item") {
			lists.appendChild(rr.removeChild(children.item(zz)));
		} else {
			zz++;
		}
	}



	// create the ordered list and the unassigned pool.
	var or = document.createElement("ol"); 
	var un = document.createElement("ul"); 
	or.style.display = "none";
	un.style.display = "none";
	lists.appendChild(or);
	lists.appendChild(un);
//	lists.style.border = "thin solid red";

	rr.controlbar = document.createElement("div");


	rr.controlbar.reset = rankleNewControl("Unrank All", function () {rankleUnrankAll(this);}, "click here to unrank all items");
//	sync = rankleNewControl("Sync", "rankleSyncInputVars(this.parentNode.parentNode);");
//	fancy = rankleNewControl("Fancy", "rankleConvertToClickable(this.parentNode.parentNode);");
//	plain = rankleNewControl("Plain", "rankleConvertToOldForm(this.parentNode.parentNode);");
	rr.controlbar.views = rankleNewControl("Fancy", function () {rankleSwitchView(this);}, "click to switch between plain/fancy");
	rr.controlbar.promote = rankleNewControl("Promote", function () {ranklePromoteAllSelected(this);}, "click to promote all selected items");
	rr.controlbar.demote = rankleNewControl("Demote", function () {rankleDemoteAllSelected(this);}, "click to demote all selected items");

	

	rr.controlbar.className = "rankle-control-bar";
	rr.controlbar.appendChild(rr.controlbar.reset);
	rr.controlbar.appendChild(document.createTextNode(" "));
//	controlbar.appendChild(sync);
//	controlbar.appendChild(fancy);
//	controlbar.appendChild(plain);
	rr.controlbar.appendChild(rr.controlbar.views);
	rr.controlbar.appendChild(document.createTextNode(" "));
	rr.controlbar.appendChild(rr.controlbar.promote);
	rr.controlbar.appendChild(document.createTextNode(" "));
	rr.controlbar.appendChild(rr.controlbar.demote);
	rr.controlbar.promote.style.display="none";
	rr.controlbar.demote.style.display="none";


//	rr.insertBefore(controlbar,rankleGetTitleForWidget(rr).nextSibling);

	rr.appendChild(rr.controlbar);
	rr.appendChild(lists);
	
	var finisher = document.createElement("div");
	finisher.style.clear = "both";
	finisher.style.margin = "0.5em";
	rr.appendChild(finisher);
//	rankleConvertToFancy(rr);
}


function rankleGetTitleForWidget(rr) {
	rankleCheckClass(rr, "rankle");
	// it's the first div with class "rankle-title".
	var ret = rr.getElementsByTagName("div").item(0);
	rankleCheckClass(ret, "rankle-title");
	return ret;	
}

// note that this will return the argument directly if the argument is itself of class cname.
function rankleGetAncestorOfClass(targ, cname, limit) {
	if (!limit) var limit = document;
	while(targ != limit) {
		if ((targ.nodeName.substr(0,1) != "#") && (targ.className) && (targ.className == cname)) {
			return targ;
		}
		targ = targ.parentNode;
	}
	rankleError("there was no ancestor with class '"+cname+"'.");
}
// note that this will return the argument directly if the argument is itself of class cname.
function rankleGetAncestorOfTagName(targ, tname, limit) {
	if (!limit) var limit = document;
	while(targ != limit) {
		if (targ.tagName == tname.toUpperCase()) {
			return targ;
		}
		targ = targ.parentNode;
	}
	rankleError("there was no ancestor with tagName '"+tname+"'.");
}


// this can take either an <li> or a rankle-item.
function rankleGetTitleSpanForItem(ritem) {
	return rankleGetDescendentWithTagClass(ritem,"span","rankle-item-title");
}

// this can take either an <li> or a rankle-item.
function rankleGetInputForItem(ritem) {
	return rankleGetDescendentWithTagClass(ritem,"input","rankle-input");
}

// this should take a full rankle widget:
function rankleGetOrderedListForWidget(rr) {
	rankleCheckClass(rr, "rankle");
	return rr.getElementsByTagName("ol").item(0);
}
// this should take a full rankle widget:
function rankleGetUnorderedListForWidget(rr) {
	rankleCheckClass(rr, "rankle");
	return rr.getElementsByTagName("ul").item(0);
}


function rankleConvertToFancy(rr) {
	var or = rankleGetOrderedListForWidget(rr);
	var un = rankleGetUnorderedListForWidget(rr);

	// display them in block form again.
	or.style.display = "block";
	un.style.display = "block";


	// for each rankle-item within the class, hide it and push it
        // into the unordered list.

	var items = rr.getElementsByTagName("div");

	for (var zz = 0; zz < items.length; zz++) {
		if (items.item(zz).className == "rankle-item") {
			items.item(zz).style.display = "none";
			var input = rankleGetInputForItem(items.item(zz));
			var title = rankleGetTitleSpanForItem(items.item(zz));

			// make sure the id of the rankle-item is set right, because we'll use it to send the data back when converting to plain.
			items.item(zz).id = input.name;
		
			var li = document.createElement("li");
			
			items.item(zz).removeChild(title);
			li.appendChild(title);
			items.item(zz).removeChild(input);
			li.appendChild(input);

			input.style.display = "none";

			var v = input.value;

			if ((v == undefined) ||
			    (v == null) ||
			    (v == "")) {
				un.appendChild(li);
			} else {
				rankleInsertRankedItem(li,or);
			}
		}
	}

	rr.controlbar.promote.style.display="inline";
	rr.controlbar.demote.style.display="inline";
	rr.controlbar.views.firstChild.nodeValue="Plain";
	rankleSyncInputVars(rr);
}

function rankleGetLiValue(rli) {
	// force a numeric return value:
	return 1*rankleGetInputForItem(rli).value;
}
function rankleGetLiName(rli) {
	return rankleGetInputForItem(rli).name;
}


function rankleInsertRankedItem(rli,or) {
	var val = rankleGetLiValue(rli);
	
	var litems = or.getElementsByTagName("li");
	for (var lzz = 0; lzz < litems.length; lzz++) {
		if (rankleGetLiValue(litems.item(lzz)) >= val) {
			or.insertBefore(rli,litems.item(lzz));
			return;
		}
	}
	// if we get here, we should just append to the list.
	or.appendChild(rli);
}


function rankleGetRankleItem(rr, id) {
	rankleCheckClass(rr, "rankle");

	var items = rr.getElementsByTagName("div");
	for (var zz = 0; zz < items.length; zz++) {
		if (items.item(zz).className == "rankle-item") {
			if (items.item(zz).id == id) {
				return items.item(zz);
			}
		}
	}
	rankleError("the rankle object " + rr.id + " didn't have a rankle-item with id " + id);
}

function rankleConvertToPlain(rr) {
	rankleCheckClass(rr, "rankle");

	rankleConvertFromList(rr, "ol", 0);
	rankleConvertFromList(rr, "ul", 0);
	rr.controlbar.promote.style.display="none";
	rr.controlbar.demote.style.display="none";
	rr.controlbar.views.firstChild.nodeValue="Fancy"; 
}



function rankleConvertFromList(rr,listtype,n) {
	rankleCheckClass(rr, "rankle");
	var list = rr.getElementsByTagName(listtype).item(n);

	list.style.display = "none";
	while ( list.childNodes.length > 0) {
		var input = rankleGetInputForItem(list.firstChild);
		var title = rankleGetTitleSpanForItem(list.firstChild);

		input.style.display = "inline";// unhides it.
		title.onclick = null; //gets rid of the click-ability.

		var targ = rankleGetRankleItem(rr, input.name);
		targ.style.display = "block";

		list.firstChild.removeChild(input);
		targ.appendChild(input);
		list.firstChild.removeChild(title);
		targ.appendChild(title);

		list.removeChild(list.firstChild);
	}
	
}

<!-- end javascript -->
