var mod_pagespeed_zEivoeGwF4 = "// client side code based on catfish ad tutorial from sitepoint\r\n// http://www.sitepoint.com/blogs/2005/10/18/the-catfish-part-1/\r\n// http://www.sitepoint.com/blogs/2005/10/21/catfish-ads-part-2/\r\n\r\n// Deploy the footer\r\n\r\n// The footer optin form should be located in an element (DIV) of id 'footer' and should be hidden\r\n// out of view (hidden by initial CSS)\r\n\r\n// Stack up window.onload events using this function from Simon Willison -\r\n// http://www.sitepoint.com/blog-post-view.php?id=171578\r\nfunction addLoadEvent(func) {\r\n	var oldonload = window.onload;\r\n	if (typeof window.onload != 'function') {\r\n		window.onload = func;\r\n	} else {\r\n		window.onload = function() {\r\n			oldonload();\r\n			func();\r\n		}\r\n	}\r\n}\r\n\r\nfunction registerHideme() {\r\n	var hidelink = document.getElementById('hideme');\r\n	hidelink.onclick = hidefooter;\r\n\r\n}\r\n\r\nfunction registerShowme() {\r\n	var showlink = document.getElementById('optincrushershow');\r\n	showlink.onclick = showfooter;\r\n}\r\n\r\naddLoadEvent(function() {\r\n	registerHideme();\r\n	registerShowme();\r\n});\r\n\r\n// lightbox functions\r\nfunction showLightBox() {\r\n	document.getElementById('light').style.display='block';\r\n	document.getElementById('fade').style.display='block'\r\n}\r\n\r\nfunction hideLightBox() {\r\n	document.getElementById('light').style.display='none';\r\n	document.getElementById('fade').style.display='none'\r\n}\r\n\r\n// layer animation\r\nvar showbuttonLayerStatus = Array ();\r\nshowbuttonLayerStatus['layerHeight'] = 0;\r\nshowbuttonLayerStatus['layerPosition'] = 0;\r\n\r\nvar footerLayerStatus = Array ();\r\nfooterLayerStatus['layerHeight'] = 0;\r\nfooterLayerStatus['layerPosition'] = 0;\r\n\r\nfunction showfooterinitial() {	\r\n	if (rememberShowHideState && getCookie('show_hide_state') == 'hide') {\r\n		setTimeout(function(){scrollLayer ('optincrushershow', showbuttonheight, 'show', footerLayerStatus)}, displayDelay);\r\n	} else{\r\n		setTimeout(function(){scrollLayer ('optincrusher', footerheight, 'show', showbuttonLayerStatus, true)}, displayDelay);		\r\n	}\r\n}\r\n\r\nfunction showfooter() {\r\n	scrollLayer ('optincrusher', footerheight, 'show', showbuttonLayerStatus, true);\r\n	scrollLayer ('optincrushershow', showbuttonheight, 'hide', footerLayerStatus);\r\n	delCookie('show_hide_state');	// delete cookie so that it shows by default\r\n}\r\n\r\nfunction hidefooter() {\r\n	scrollLayer ('optincrusher', footerheight, 'hide', showbuttonLayerStatus, true);\r\n	scrollLayer ('optincrushershow', showbuttonheight, 'show', footerLayerStatus);\r\n	setCookie('show_hide_state','hide','/',31);	// don't show for a month, unless then show it\r\n}\r\n\r\nfunction scrollLayer ( whichLayer, height, action, statusArray, modifyBottomPadding ) {\r\n	// get handle for the div to animate\r\n	statusArray['layerToScroll'] = document.getElementById(whichLayer);\r\n\r\n	// begin animation\r\n	statusArray['layerHeight'] = height;\r\n	statusArray['layerPosition'] = (action == 'show') ? 0:100; // statusArray['layerPosition'] is expressed as a percentage (out of 100);\r\n	statusArray['layerTimeout'] = setInterval(function(){positionLayer(action, statusArray, modifyBottomPadding)}, slidespeed);\r\n}\r\n\r\n// position the layer (see scrollLayer which initializes the timeout for this function call)\r\nfunction positionLayer(action, statusArray, modifyBottomPadding) {\r\n	if (action == 'show') {\r\n		statusArray['layerPosition'] += slidestep;\r\n	} else if (action == 'hide') {\r\n		statusArray['layerPosition'] -= slidestep;\r\n	}\r\n\r\n	// the magic 100s below indicate percentage with the layer approaching 100% shown/hidden\r\n	statusArray['layerToScroll'].style.marginBottom = '-' + (((100 - statusArray['layerPosition']) / 100) * statusArray['layerHeight']) + 'px';\r\n\r\n	// if I'm 100% deployed then finish footer\r\n	if ((action == 'show' && statusArray['layerPosition'] >= 100) || (action == 'hide' && statusArray['layerPosition'] <= 0)) {\r\n		clearTimeout(statusArray['layerTimeout']);\r\n		if (modifyBottomPadding) statusArray['layerTimeout'] = setTimeout(function(){finishLayer(action, statusArray)}, 1);\r\n	}\r\n}\r\n\r\nfunction finishLayer(action, statusArray) {\r\n	// just in case we overdeployed in positionfooter(), ensure marginBottom is zero (0)\r\n	statusArray['layerToScroll'].style.marginBottom = (action == 'hide') ? '-'+statusArray['layerHeight']+'px':'0px';\r\n\r\n	// increase padding at bottom of document so that page content isn't hidden under\r\n	// the footer when the page is scrolled all the way to the bottom\r\n	document.body.parentNode.style.paddingBottom = (action == 'hide') ? attributionoffset + 'px':(statusArray['layerHeight'] - footeroverlap + attributionoffset) +'px';\r\n\r\n	// here you could use AJAX (or similar) to log the popup hit for tracking purposes\r\n}\r\n\r\naddLoadEvent(showfooterinitial);\r\n// cookie management functions below\r\n\r\nfunction setCookie(c_name,value,path,expiredays) {\r\n	var exdate = new Date();\r\n	exdate.setDate(exdate.getDate()+expiredays);\r\n	document.cookie = c_name + \"=\" + escape(value) + (path ? '; path=' + path : '') + ((expiredays==null) ? \"\" : \";expires=\" + exdate.toGMTString());\r\n}\r\n\r\nfunction getCookie(c_name) {\r\n	if (document.cookie.length>0) {\r\n		c_start=document.cookie.indexOf(c_name + \"=\");\r\n		if (c_start!=-1) {\r\n			c_start=c_start + c_name.length+1;\r\n			c_end=document.cookie.indexOf(\";\",c_start);\r\n			if (c_end==-1) c_end=document.cookie.length;\r\n			return unescape(document.cookie.substring(c_start,c_end));\r\n		}\r\n	}\r\n	return \"\";\r\n}\r\n\r\n// this deletes the cookie when called\r\nfunction delCookie(c_name) {\r\n	var tmp = getCookie(c_name);\r\n	if(tmp) { setCookie(c_name,tmp,'/',-1); }\r\n}";
var mod_pagespeed_HINela9stR = "function MPGObject() {\n	this.isID = false;\n	this.isAll = false;\n	this.isLayer = false;\n	this.home_url = \"http://www.mpgomatic.com/best_gas_mileage_car.html\";\n	this.base_url = \"http://www.mpgomatic.com/mpgomatic/\";\n	\n	if(document.getElementById) this.isID = true;\n	else {\n		if(document.all) this.isAll = true;\n		else {\n			if((navigator.appName.indexOf('Netscape') != -1) && (parseInt(navigator.appVersion) == 4)) this.isLayer = true;\n		}\n	}\n}\nvar MPG = new MPGObject();\n\nMPGObject.prototype.$ = function(pElementID) {\n	if(MPG.isID) return document.getElementById(pElementID);\n	if(MPG.isAll) return document.all[pElementID]; \n	if(MPG.isLayer) return document.layers[pElementID];\n	return null;\n}\nMPGObject.prototype.$$ = function(pElementID) {\n	var vObject = MPG.$(pElementID);\n	if(!vObject) return null;\n	if(!vObject.style) return vObject;\n	if(MPG.isID || (!MPG.isID && MPG.isAll)) return (vObject.style);\n	return vObject;\n}\nMPGObject.prototype.$A = function(pObject, pEvent, pFc) {\n	if(pObject.addEventListener) pObject.addEventListener(pEvent, pFc, false); \n	else if(pObject.attachEvent) pObject.attachEvent(\"on\" + pEvent, pFc);\n}\nMPGObject.prototype.getCookie = function(pName, pDefault) {\n	var vStart = document.cookie.indexOf(pName + \"=\");\n	var vValueSeparatorIndex = (pName + \"=\").indexOf(\"=\") + 1;\n	if(vStart >= 0) {\n		var vEnd = document.cookie.indexOf(\";\", vStart);\n		if(vEnd < 0) vEnd = document.cookie.length;\n		return document.cookie.substring((vStart + vValueSeparatorIndex), vEnd);\n	}\n	return pDefault;\n}\nMPGObject.prototype.setCookie = function(pName, pValue, pExpireHours) {\n	document.cookie = pName + \"=\" + escape(pValue) + \"; expires=\" + (new Date((new Date()).getTime() + (pExpireHours * 3600000))).toGMTString();\n}\n\n/*\n<head>\n	<link rel=\"stylesheet\" href=\"http://www.mpgomatic.com/mpgomatic/mpgomatic.css\" type=\"text/css\" />\n	<script src=\"http://www.mpgomatic.com/mpgomatic/mpgomatic.js\" type=\"text/javascript><\/script>\n</head>\n<body>\n...\n<div id=\"mpgomatic\"></div><script type=\"text/javascript\">MPG.load(\"mpgomatic\");<\/script>\n...\n</body>\n*/\nMPGObject.prototype.load = function(pElementID) {\n	try {\n		var vContainer = MPG.$(pElementID);\n		if(vContainer) {\n			for(var vLoop = (vContainer.childNodes.length - 1); vLoop >= 0; vLoop--) vContainer.removeChild(vContainer.childNodes[vLoop]);\n			\n			vContainer.className = \"mpgomatic\";\n			\n			var vObject = document.createElement(\"img\");\n			vContainer.appendChild(vObject);\n			vObject.id = \"mpg_units_button\";\n			vObject.src = MPG.base_url + \"art/mpg.png\";\n			MPG.$A(vObject, \"click\", MPG.toggle_units);\n			\n			vObject = document.createElement(\"img\");\n			vContainer.appendChild(vObject);\n			vObject.id = \"mpg_units\";\n			vObject.src = MPG.base_url + \"art/kpl_head.png\";\n			if(MPG.getCookie(\"units\", \"mpg\") == \"kpl\") MPG.toggle_units();\n			\n			var vForm = document.createElement(\"form\");\n			vContainer.appendChild(vForm);\n			vForm.action = \"#\";\n			vForm.onsubmit = \"return false;\";\n			\n			vObject = document.createElement(\"input\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_ocpg\";\n			vObject.className = \"mpg_field\";\n			vObject.type = \"text\";\n			\n			vObject = document.createElement(\"input\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_ompg\";\n			vObject.className = \"mpg_field\";\n			vObject.type = \"text\";\n			\n			vObject = document.createElement(\"input\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_omd\";\n			vObject.className = \"mpg_field\";\n			vObject.type = \"text\";\n			\n			vObject = document.createElement(\"div\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_ocpm\";\n			vObject.className = \"mpg_field mpg_calc_field\";\n			\n			vObject = document.createElement(\"div\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_otc\";\n			vObject.className = \"mpg_field mpg_calc_field\";\n			\n			\n			vObject = document.createElement(\"input\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_ncpg\";\n			vObject.className = \"mpg_field\";\n			vObject.type = \"text\";\n			\n			vObject = document.createElement(\"input\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_nmpg\";\n			vObject.className = \"mpg_field\";\n			vObject.type = \"text\";\n			\n			vObject = document.createElement(\"input\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_nmd\";\n			vObject.className = \"mpg_field\";\n			vObject.type = \"text\";\n			\n			vObject = document.createElement(\"div\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_ncpm\";\n			vObject.className = \"mpg_field mpg_calc_field\";\n			\n			vObject = document.createElement(\"div\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_ntc\";\n			vObject.className = \"mpg_field mpg_calc_field\";\n			\n			\n			vObject = document.createElement(\"div\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_scpm\";\n			vObject.className = \"mpg_field mpg_savings_field\";\n			\n			vObject = document.createElement(\"div\");\n			vForm.appendChild(vObject);\n			vObject.id = \"mpg_stc\";\n			vObject.className = \"mpg_field mpg_savings_field\";\n			\n			MPG.make_field(\"mpg_ocpg\");\n			MPG.make_field(\"mpg_ompg\");\n			MPG.make_field(\"mpg_omd\");\n			MPG.make_field(\"mpg_ncpg\");\n			MPG.make_field(\"mpg_nmpg\");\n			MPG.make_field(\"mpg_nmd\");\n			\n			vObject = document.createElement(\"img\");\n			vContainer.appendChild(vObject);\n			vObject.id = \"mpg_savings\";\n			vObject.src = MPG.base_url + \"art/savings_grey.png\";\n			\n			vObject = document.createElement(\"a\");\n			vContainer.appendChild(vObject);\n			vObject.id = \"mpg_goto_site\";\n			vObject.href = MPG.home_url;\n			vObject.target = \"_blank\";\n			MPG.$A(vObject, \"mouseover\", function(pEvent) { MPG.$(\"mpg_goto_site_img\").src = MPG.base_url + \"art/button_hover.png\"; });\n			MPG.$A(vObject, \"mouseout\", function(pEvent) { MPG.$(\"mpg_goto_site_img\").src = MPG.base_url + \"art/button.png\"; });\n			//MPG.$A(vObject, \"click\", function(pEvent) { MPG.$(\"mpg_goto_site_img\").src = MPG.base_url + \"art/button_click.png\"; document.location = MPG.home_url; MPG.$(\"mpg_goto_site\").src = MPG.base_url + \"art/button.png\"; });\n			\n			var vImage = document.createElement(\"img\");\n			vObject.appendChild(vImage);\n			vImage.id = \"mpg_goto_site_img\";\n			vImage.src = MPG.base_url + \"art/button.png\";\n		}\n	} catch(e) { }\n}\n\nMPGObject.prototype.toggle_units = function() {\n	var vObject = MPG.$(\"mpg_units_button\");\n	if(vObject.src.match(/kpl.png$/i)) {\n		vObject.src = MPG.base_url + \"art/mpg.png\";\n		MPG.$(\"mpg_units\").style.visibility = \"hidden\";\n		MPG.setCookie(\"units\", \"mpg\", (30 * 24));\n	} else {\n		vObject.src = MPG.base_url + \"art/kpl.png\";\n		MPG.$(\"mpg_units\").style.visibility = \"visible\";\n		MPG.setCookie(\"units\", \"kpl\", (30 * 24));\n	}\n	\n}\nMPGObject.prototype.make_field = function(pObjectID) {\n	var vObject = MPG.$(pObjectID);\n	if(vObject) {\n		MPG.$A(vObject, \"blur\", function(pEvent) {\n			vObject.value = vObject.value.to_field_value((vObject.id.match(/cpg$/)) ? \"$\" : \"\");\n			vObject.num_value = vObject.value.to_numerical_value();\n			\n			if((vObject.id == \"mpg_ocpg\") || (vObject.id == \"mpg_omd\")) {\n				var vRelation = MPG.$(vObject.id.replace(/_o/, \"_n\"));\n				if(vRelation) {\n					vRelation.value = vObject.value\n					vRelation.num_value = vObject.num_value;\n				}\n			}\n			MPG.recalculate_values();\n		}, true);\n	}\n}\nMPGObject.prototype.setInnerText = function(pObject, pValue) {\n	if(pObject) {\n		for(var vLoop = (pObject.childNodes.length - 1); vLoop >= 0; vLoop--) pObject.removeChild(pObject.childNodes[vLoop]);\n		pObject.appendChild(document.createTextNode(pValue));\n	}\n}\n\nMPGObject.prototype.recalculate_values = function() {\n	var vOldValues = MPG.recalculate_line_values(\"o\");\n	var vNewValues = MPG.recalculate_line_values(\"n\");\n	\n	if((vOldValues.length >= 3) && (vNewValues.length >= 3)) {\n		var vObject = MPG.$(\"mpg_scpm\");\n		if(vObject) MPG.setInnerText(vObject, (\"\" + (vOldValues[3] - vNewValues[3])).to_field_value(\"$$\"));\n		vObject = MPG.$(\"mpg_stc\");\n		if(vObject) MPG.setInnerText(vObject, (\"\" + (vOldValues[4] - vNewValues[4])).to_field_value(\"$-\"));\n		MPG.$(\"mpg_savings\").src = MPG.base_url + \"art/savings.png\";\n	} else {\n		var vObject = MPG.$(\"mpg_scpm\");\n		if(vObject) MPG.setInnerText(vObject, \"\");\n		vObject = MPG.$(\"mpg_stc\");\n		if(vObject) MPG.setInnerText(vObject, \"\");\n		MPG.$(\"mpg_savings\").src = MPG.base_url + \"art/savings_grey.png\";\n	}\n}\nMPGObject.prototype.recalculate_line_values = function(pPrefix) {\n	var vResult = new Array();\n	var vObject = MPG.$(\"mpg_\" + pPrefix + \"cpg\");\n	if(vObject && !isNaN(vObject.num_value)) vResult[vResult.length] = vObject.num_value;\n	vObject = MPG.$(\"mpg_\" + pPrefix + \"mpg\");\n	if(vObject && !isNaN(vObject.num_value)) vResult[vResult.length] = vObject.num_value;\n	vObject = MPG.$(\"mpg_\" + pPrefix + \"md\");\n	if(vObject && !isNaN(vObject.num_value)) vResult[vResult.length] = vObject.num_value;\n	\n	if(vResult.length >= 3) {\n		vObject = MPG.$(\"mpg_\" + pPrefix + \"cpm\");\n		if(vObject) {\n			vResult[vResult.length] = (\"\" + (vResult[0] / vResult[1])).to_numerical_value(true);\n			MPG.setInnerText(vObject, (\"\" + vResult[3]).to_field_value(\"$$\"));\n		}\n		vObject = MPG.$(\"mpg_\" + pPrefix + \"tc\");\n		if(vObject) {\n			vResult[vResult.length] = (\"\" + (vResult[3] * vResult[2])).to_numerical_value();\n			MPG.setInnerText(vObject, (\"\" + vResult[4]).to_field_value(\"$-\"));\n		}\n	} else {\n		vObject = MPG.$(\"mpg_\" + pPrefix + \"cpm\");\n		if(vObject) MPG.setInnerText(vObject, \"\");\n		vObject = MPG.$(\"mpg_\" + pPrefix + \"tc\");\n		if(vObject) MPG.setInnerText(vObject, \"\");\n	}\n	\n	return vResult;\n}\n\nString.prototype.to_numerical_value = function(pLong) {\n	if(this == \"\") return Number.isNaN;\n	var vValue = this.replace(/[$,]/, \"\");\n	var vTokens = (pLong ? vValue.match(/(\\d+)\\.(\\d{4})\\d*/) : vValue.match(/(\\d+)\\.(\\d{3})\\d*/));\n	if(!vTokens || (vTokens.length < 3)) return parseFloat(vValue);\n	vValue = parseInt(vTokens[1]);\n	var vLastDigit = (pLong ? 3 : 2);\n	if(parseInt(vTokens[2].substr(vLastDigit)) >= 5) {\n		if(pLong) vValue += (parseFloat(\"0.\" + vTokens[2].substr(0, vLastDigit)) + 0.001);\n		else vValue += (parseFloat(\"0.\" + vTokens[2].substr(0, vLastDigit)) + 0.01);\n	} else vValue += (parseFloat(\"0.\" + vTokens[2].substr(0,vLastDigit)));\n	return parseFloat(vValue);\n}\nString.prototype.to_field_value = function(pType) {\n	var vNumber = this.to_numerical_value((pType == \"$$\"));\n	if(isNaN(vNumber)) return \"\";\n	var vValue = (\"\" + vNumber);\n	if(pType == \"$-\") return \"$\" + vNumber.toFixed(0).number_format();\n	if((pType == \"$\") || (pType == \"$$\")) return \"$\" + vNumber.toFixed(((pType == \"$$\") ? 3 : 2)).number_format();\n	return vValue.number_format();\n}\nString.prototype.number_format = function() {\n	var vTokens = this.split('.');\n	var vWhole = vTokens[0];\n	var vDecimals = vTokens.length > 1 ? '.' + vTokens[1] : '';\n	var vRE = /(\\d+)(\\d{3})/;\n	while (vRE.test(vWhole)) vWhole = vWhole.replace(vRE, '$1,$2');\n	if(vWhole == \"\") vWhole = \"0\";\n	return vWhole + vDecimals;\n}\n\n";
var mod_pagespeed_QOgoWwxflK = "/*\n  SortTable\n  version 2\n  7th April 2007\n  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/\n  \n  Instructions:\n  Download this file\n  Add <script src=\"sorttable.js\"><\/script> to your HTML\n  Add class=\"sortable\" to any table you'd like to make sortable\n  Click on the headers to sort\n  \n  Thanks to many, many people for contributions and suggestions.\n  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html\n  This basically means: do what you want with it.\n*/\n\n \nvar stIsIE = /*@cc_on!@*/false;\n\nsorttable = {\n  init: function() {\n    // quit if this function has already been called\n    if (arguments.callee.done) return;\n    // flag this function so we don't do the same thing twice\n    arguments.callee.done = true;\n    // kill the timer\n    if (_timer) clearInterval(_timer);\n    \n    if (!document.createElement || !document.getElementsByTagName) return;\n    \n    sorttable.DATE_RE = /^(\\d\\d?)[\\/\\.-](\\d\\d?)[\\/\\.-]((\\d\\d)?\\d\\d)$/;\n    \n    forEach(document.getElementsByTagName('table'), function(table) {\n      if (table.className.search(/\\bsortable\\b/) != -1) {\n        sorttable.makeSortable(table);\n      }\n    });\n    \n  },\n  \n  makeSortable: function(table) {\n    if (table.getElementsByTagName('thead').length == 0) {\n      // table doesn't have a tHead. Since it should have, create one and\n      // put the first table row in it.\n      the = document.createElement('thead');\n      the.appendChild(table.rows[0]);\n      table.insertBefore(the,table.firstChild);\n    }\n    // Safari doesn't support table.tHead, sigh\n    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];\n    \n    if (table.tHead.rows.length != 1) return; // can't cope with two header rows\n    \n    // Sorttable v1 put rows with a class of \"sortbottom\" at the bottom (as\n    // \"total\" rows, for example). This is B&R, since what you're supposed\n    // to do is put them in a tfoot. So, if there are sortbottom rows,\n    // for backwards compatibility, move them to tfoot (creating it if needed).\n    sortbottomrows = [];\n    for (var i=0; i<table.rows.length; i++) {\n      if (table.rows[i].className.search(/\\bsortbottom\\b/) != -1) {\n        sortbottomrows[sortbottomrows.length] = table.rows[i];\n      }\n    }\n    if (sortbottomrows) {\n      if (table.tFoot == null) {\n        // table doesn't have a tfoot. Create one.\n        tfo = document.createElement('tfoot');\n        table.appendChild(tfo);\n      }\n      for (var i=0; i<sortbottomrows.length; i++) {\n        tfo.appendChild(sortbottomrows[i]);\n      }\n      delete sortbottomrows;\n    }\n    \n    // work through each column and calculate its type\n    headrow = table.tHead.rows[0].cells;\n    for (var i=0; i<headrow.length; i++) {\n      // manually override the type with a sorttable_type attribute\n      if (!headrow[i].className.match(/\\bsorttable_nosort\\b/)) { // skip this col\n        mtch = headrow[i].className.match(/\\bsorttable_([a-z0-9]+)\\b/);\n        if (mtch) { override = mtch[1]; }\n	      if (mtch && typeof sorttable[\"sort_\"+override] == 'function') {\n	        headrow[i].sorttable_sortfunction = sorttable[\"sort_\"+override];\n	      } else {\n	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);\n	      }\n	      // make it clickable to sort\n	      headrow[i].sorttable_columnindex = i;\n	      headrow[i].sorttable_tbody = table.tBodies[0];\n	      dean_addEvent(headrow[i],\"click\", function(e) {\n\n          if (this.className.search(/\\bsorttable_sorted\\b/) != -1) {\n            // if we're already sorted by this column, just \n            // reverse the table, which is quicker\n            sorttable.reverse(this.sorttable_tbody);\n            this.className = this.className.replace('sorttable_sorted',\n                                                    'sorttable_sorted_reverse');\n            this.removeChild(document.getElementById('sorttable_sortfwdind'));\n            sortrevind = document.createElement('span');\n            sortrevind.id = \"sorttable_sortrevind\";\n            sortrevind.innerHTML = stIsIE ? '&nbsp<font face=\"webdings\">5</font>' : '&nbsp;&#x25B4;';\n            this.appendChild(sortrevind);\n            return;\n          }\n          if (this.className.search(/\\bsorttable_sorted_reverse\\b/) != -1) {\n            // if we're already sorted by this column in reverse, just \n            // re-reverse the table, which is quicker\n            sorttable.reverse(this.sorttable_tbody);\n            this.className = this.className.replace('sorttable_sorted_reverse',\n                                                    'sorttable_sorted');\n            this.removeChild(document.getElementById('sorttable_sortrevind'));\n            sortfwdind = document.createElement('span');\n            sortfwdind.id = \"sorttable_sortfwdind\";\n            sortfwdind.innerHTML = stIsIE ? '&nbsp<font face=\"webdings\">6</font>' : '&nbsp;&#x25BE;';\n            this.appendChild(sortfwdind);\n            return;\n          }\n          \n          // remove sorttable_sorted classes\n          theadrow = this.parentNode;\n          forEach(theadrow.childNodes, function(cell) {\n            if (cell.nodeType == 1) { // an element\n              cell.className = cell.className.replace('sorttable_sorted_reverse','');\n              cell.className = cell.className.replace('sorttable_sorted','');\n            }\n          });\n          sortfwdind = document.getElementById('sorttable_sortfwdind');\n          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }\n          sortrevind = document.getElementById('sorttable_sortrevind');\n          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }\n          \n          this.className += ' sorttable_sorted';\n          sortfwdind = document.createElement('span');\n          sortfwdind.id = \"sorttable_sortfwdind\";\n          sortfwdind.innerHTML = stIsIE ? '&nbsp<font face=\"webdings\">6</font>' : '&nbsp;&#x25BE;';\n          this.appendChild(sortfwdind);\n\n	        // build an array to sort. This is a Schwartzian transform thing,\n	        // i.e., we \"decorate\" each row with the actual sort key,\n	        // sort based on the sort keys, and then put the rows back in order\n	        // which is a lot faster because you only do getInnerText once per row\n	        row_array = [];\n	        col = this.sorttable_columnindex;\n	        rows = this.sorttable_tbody.rows;\n	        for (var j=0; j<rows.length; j++) {\n	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];\n	        }\n	        /* If you want a stable sort, uncomment the following line */\n	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);\n	        /* and comment out this one */\n	        row_array.sort(this.sorttable_sortfunction);\n	        \n	        tb = this.sorttable_tbody;\n	        for (var j=0; j<row_array.length; j++) {\n	          tb.appendChild(row_array[j][1]);\n	        }\n	        \n	        delete row_array;\n	      });\n	    }\n    }\n  },\n  \n  guessType: function(table, column) {\n    // guess the type of a column based on its first non-blank row\n    sortfn = sorttable.sort_alpha;\n    for (var i=0; i<table.tBodies[0].rows.length; i++) {\n      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);\n      if (text != '') {\n        if (text.match(/^-?[£$¤]?[\\d,.]+%?$/)) {\n          return sorttable.sort_numeric;\n        }\n        // check for a date: dd/mm/yyyy or dd/mm/yy \n        // can have / or . or - as separator\n        // can be mm/dd as well\n        possdate = text.match(sorttable.DATE_RE)\n        if (possdate) {\n          // looks like a date\n          first = parseInt(possdate[1]);\n          second = parseInt(possdate[2]);\n          if (first > 12) {\n            // definitely dd/mm\n            return sorttable.sort_ddmm;\n          } else if (second > 12) {\n            return sorttable.sort_mmdd;\n          } else {\n            // looks like a date, but we can't tell which, so assume\n            // that it's dd/mm (English imperialism!) and keep looking\n            sortfn = sorttable.sort_ddmm;\n          }\n        }\n      }\n    }\n    return sortfn;\n  },\n  \n  getInnerText: function(node) {\n    // gets the text we want to use for sorting for a cell.\n    // strips leading and trailing whitespace.\n    // this is *not* a generic getInnerText function; it's special to sorttable.\n    // for example, you can override the cell text with a customkey attribute.\n    // it also gets .value for <input> fields.\n    \n    hasInputs = (typeof node.getElementsByTagName == 'function') &&\n                 node.getElementsByTagName('input').length;\n    \n    if (node.getAttribute(\"sorttable_customkey\") != null) {\n      return node.getAttribute(\"sorttable_customkey\");\n    }\n    else if (typeof node.textContent != 'undefined' && !hasInputs) {\n      return node.textContent.replace(/^\\s+|\\s+$/g, '');\n    }\n    else if (typeof node.innerText != 'undefined' && !hasInputs) {\n      return node.innerText.replace(/^\\s+|\\s+$/g, '');\n    }\n    else if (typeof node.text != 'undefined' && !hasInputs) {\n      return node.text.replace(/^\\s+|\\s+$/g, '');\n    }\n    else {\n      switch (node.nodeType) {\n        case 3:\n          if (node.nodeName.toLowerCase() == 'input') {\n            return node.value.replace(/^\\s+|\\s+$/g, '');\n          }\n        case 4:\n          return node.nodeValue.replace(/^\\s+|\\s+$/g, '');\n          break;\n        case 1:\n        case 11:\n          var innerText = '';\n          for (var i = 0; i < node.childNodes.length; i++) {\n            innerText += sorttable.getInnerText(node.childNodes[i]);\n          }\n          return innerText.replace(/^\\s+|\\s+$/g, '');\n          break;\n        default:\n          return '';\n      }\n    }\n  },\n  \n  reverse: function(tbody) {\n    // reverse the rows in a tbody\n    newrows = [];\n    for (var i=0; i<tbody.rows.length; i++) {\n      newrows[newrows.length] = tbody.rows[i];\n    }\n    for (var i=newrows.length-1; i>=0; i--) {\n       tbody.appendChild(newrows[i]);\n    }\n    delete newrows;\n  },\n  \n  /* sort functions\n     each sort function takes two parameters, a and b\n     you are comparing a[0] and b[0] */\n  sort_numeric: function(a,b) {\n    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));\n    if (isNaN(aa)) aa = 0;\n    bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); \n    if (isNaN(bb)) bb = 0;\n    return aa-bb;\n  },\n  sort_alpha: function(a,b) {\n    if (a[0]==b[0]) return 0;\n    if (a[0]<b[0]) return -1;\n    return 1;\n  },\n  sort_ddmm: function(a,b) {\n    mtch = a[0].match(sorttable.DATE_RE);\n    y = mtch[3]; m = mtch[2]; d = mtch[1];\n    if (m.length == 1) m = '0'+m;\n    if (d.length == 1) d = '0'+d;\n    dt1 = y+m+d;\n    mtch = b[0].match(sorttable.DATE_RE);\n    y = mtch[3]; m = mtch[2]; d = mtch[1];\n    if (m.length == 1) m = '0'+m;\n    if (d.length == 1) d = '0'+d;\n    dt2 = y+m+d;\n    if (dt1==dt2) return 0;\n    if (dt1<dt2) return -1;\n    return 1;\n  },\n  sort_mmdd: function(a,b) {\n    mtch = a[0].match(sorttable.DATE_RE);\n    y = mtch[3]; d = mtch[2]; m = mtch[1];\n    if (m.length == 1) m = '0'+m;\n    if (d.length == 1) d = '0'+d;\n    dt1 = y+m+d;\n    mtch = b[0].match(sorttable.DATE_RE);\n    y = mtch[3]; d = mtch[2]; m = mtch[1];\n    if (m.length == 1) m = '0'+m;\n    if (d.length == 1) d = '0'+d;\n    dt2 = y+m+d;\n    if (dt1==dt2) return 0;\n    if (dt1<dt2) return -1;\n    return 1;\n  },\n  \n  shaker_sort: function(list, comp_func) {\n    // A stable sort function to allow multi-level sorting of data\n    // see: http://en.wikipedia.org/wiki/Cocktail_sort\n    // thanks to Joseph Nahmias\n    var b = 0;\n    var t = list.length - 1;\n    var swap = true;\n\n    while(swap) {\n        swap = false;\n        for(var i = b; i < t; ++i) {\n            if ( comp_func(list[i], list[i+1]) > 0 ) {\n                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;\n                swap = true;\n            }\n        } // for\n        t--;\n\n        if (!swap) break;\n\n        for(var i = t; i > b; --i) {\n            if ( comp_func(list[i], list[i-1]) < 0 ) {\n                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;\n                swap = true;\n            }\n        } // for\n        b++;\n\n    } // while(swap)\n  }  \n}\n\n/* ******************************************************************\n   Supporting functions: bundled here to avoid depending on a library\n   ****************************************************************** */\n\n// Dean Edwards/Matthias Miller/John Resig\n\n/* for Mozilla/Opera9 */\nif (document.addEventListener) {\n    document.addEventListener(\"DOMContentLoaded\", sorttable.init, false);\n}\n\n/* for Internet Explorer */\n/*@cc_on @*/\n/*@if (@_win32)\n    document.write(\"<script id=__ie_onload defer src=javascript:void(0)><\\\/script>\");\n    var script = document.getElementById(\"__ie_onload\");\n    script.onreadystatechange = function() {\n        if (this.readyState == \"complete\") {\n            sorttable.init(); // call the onload handler\n        }\n    };\n/*@end @*/\n\n/* for Safari */\nif (/WebKit/i.test(navigator.userAgent)) { // sniff\n    var _timer = setInterval(function() {\n        if (/loaded|complete/.test(document.readyState)) {\n            sorttable.init(); // call the onload handler\n        }\n    }, 10);\n}\n\n/* for other browsers */\nwindow.onload = sorttable.init;\n\n// written by Dean Edwards, 2005\n// with input from Tino Zijdel, Matthias Miller, Diego Perini\n\n// http://dean.edwards.name/weblog/2005/10/add-event/\n\nfunction dean_addEvent(element, type, handler) {\n	if (element.addEventListener) {\n		element.addEventListener(type, handler, false);\n	} else {\n		// assign each event handler a unique ID\n		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;\n		// create a hash table of event types for the element\n		if (!element.events) element.events = {};\n		// create a hash table of event handlers for each element/event pair\n		var handlers = element.events[type];\n		if (!handlers) {\n			handlers = element.events[type] = {};\n			// store the existing event handler (if there is one)\n			if (element[\"on\" + type]) {\n				handlers[0] = element[\"on\" + type];\n			}\n		}\n		// store the event handler in the hash table\n		handlers[handler.$$guid] = handler;\n		// assign a global event handler to do all the work\n		element[\"on\" + type] = handleEvent;\n	}\n};\n// a counter used to create unique IDs\ndean_addEvent.guid = 1;\n\nfunction removeEvent(element, type, handler) {\n	if (element.removeEventListener) {\n		element.removeEventListener(type, handler, false);\n	} else {\n		// delete the event handler from the hash table\n		if (element.events && element.events[type]) {\n			delete element.events[type][handler.$$guid];\n		}\n	}\n};\n\nfunction handleEvent(event) {\n	var returnValue = true;\n	// grab the event object (IE uses a global event object)\n	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);\n	// get a reference to the hash table of event handlers\n	var handlers = this.events[event.type];\n	// execute each event handler\n	for (var i in handlers) {\n		this.$$handleEvent = handlers[i];\n		if (this.$$handleEvent(event) === false) {\n			returnValue = false;\n		}\n	}\n	return returnValue;\n};\n\nfunction fixEvent(event) {\n	// add W3C standard event methods\n	event.preventDefault = fixEvent.preventDefault;\n	event.stopPropagation = fixEvent.stopPropagation;\n	return event;\n};\nfixEvent.preventDefault = function() {\n	this.returnValue = false;\n};\nfixEvent.stopPropagation = function() {\n  this.cancelBubble = true;\n}\n\n// Dean's forEach: http://dean.edwards.name/base/forEach.js\n/*\n	forEach, version 1.0\n	Copyright 2006, Dean Edwards\n	License: http://www.opensource.org/licenses/mit-license.php\n*/\n\n// array-like enumeration\nif (!Array.forEach) { // mozilla already supports this\n	Array.forEach = function(array, block, context) {\n		for (var i = 0; i < array.length; i++) {\n			block.call(context, array[i], i, array);\n		}\n	};\n}\n\n// generic enumeration\nFunction.prototype.forEach = function(object, block, context) {\n	for (var key in object) {\n		if (typeof this.prototype[key] == \"undefined\") {\n			block.call(context, object[key], key, object);\n		}\n	}\n};\n\n// character enumeration\nString.forEach = function(string, block, context) {\n	Array.forEach(string.split(\"\"), function(chr, index) {\n		block.call(context, chr, index, string);\n	});\n};\n\n// globally resolve forEach enumeration\nvar forEach = function(object, block, context) {\n	if (object) {\n		var resolve = Object; // default\n		if (object instanceof Function) {\n			// functions have a \"length\" property\n			resolve = Function;\n		} else if (object.forEach instanceof Function) {\n			// the object implements a custom forEach method so use that\n			object.forEach(block, context);\n			return;\n		} else if (typeof object == \"string\") {\n			// the object is a string\n			resolve = String;\n		} else if (typeof object.length == \"number\") {\n			// the object is array-like\n			resolve = Array;\n		}\n		resolve.forEach(object, block, context);\n	}\n};\n\n";

