/*--------------------------------------------------------
 *  Function:  getObject
 *  
 *  Description:
 *  Accepts an object reference or a string, returns an
 *  object reference
 *  
 *  Parameters:
 *  pRef	string or object	ID of HTML DOM entity, or
 *								reference to an object
 *	Exceptions:
 *	ObjectNotFoundException
 *	InvalidObjectRefException
 *  
 *  Return:
 *  object		Reference to named or passed DOM object.
 *-------------------------------------------------------*/

function getObject(pRef) {
	var obj;

	if (typeof(pRef) == "object") {
		obj = pRef;
	} else if (typeof(pRef) == "string") {
		obj = document.getElementById(pRef);
		if (!obj) {
			throw new ObjectNotFoundException(pRef);
		}
	} else {
		throw new InvalidObjectRefException(pRef);
	}
	
	return obj;
}

/*--------------------------------------------------------
 *  Function:  filterByClassName
 *  
 *  Description:
 *  Filters an array of DOM elements, returning only
 *  elements that match a particular class name.
 *  
 *  Parameters:
 *  pObjects	array	Array of DOM elements to filter
 *  pClassName	string	The class name to match
 *  
 *  Return:
 *  array		An array of DOM objects with the matching
 *				class name.
 *-------------------------------------------------------*/

function filterByClassName(pObjects, pClassName) {
	var elements = new Array();

	for (var i = 0; i < pObjects.length; i++) {
		var obj = pObjects[i];
		if (obj.className) {
			var classNames = obj.className.split(' ');
			for (var j = 0; j < classNames.length; j++) {
				if (classNames[j] == pClassName) {
					elements[elements.length] = obj;
					break;
				}
			}
		}
	}
  
	return elements;
}

/*--------------------------------------------------------
 *  Function:  getObjectsByClassName
 *  
 *  Description:
 *  Returns an array of DOM objects that match a particular
 *  class name.
 *  
 *  Parameters:
 *  pClassName	string	The class name to match
 *  
 *  Return:
 *  array		An array of DOM objects with the matching
 *				class name.
 *-------------------------------------------------------*/

function getObjectsByClassName(pClassName) {
	var all = document.getElementsByTagName('*') || document.all;
	
	return filterByClassName(all, pClassName);
}

/*--------------------------------------------------------
 *  Function:  getObjectsByClassName
 *  
 *  Description:
 *  Returns an array of DOM objects that match a particular
 *  class name that are a descendent of pParentId
 *  
 *  Parameters:
 *  pParentId	string	The id of a parent node to start search
 *  pClassName	string	The class name to match
 *  
 *  Return:
 *  array		An array of DOM objects with the matching
 *				class name.
 *-------------------------------------------------------*/

function getChildObjectsByClassName(pParentId,pClassName) {
	var parent = document.getElementById(pParentId);
	var all = parent.getElementsByTagName('*');
	return filterByClassName(all,pClassName);
}

/*--------------------------------------------------------
 *  Function:  getChildrenByClassName
 *  
 *  Description:
 *  Accepts a reference to a DOM object and returns an array
 *  of child elements that match a particular class name.
 *  
 *  Parameters:
 *  pRef		string or object	The parent DOM element
 *									to search
 *  pClassName	string				The class name to match
 *  
 *  Return:
 *  array		An array of DOM objects with the matching
 *				class name.
 *-------------------------------------------------------*/

function getChildrenByClassName(pRef, pClassName) {
	var children = getObject(pRef).childNodes;

	return filterByClassName(children, pClassName);
}

/*--------------------------------------------------------
 *  Function:  getDescendantsByClassName
 *  
 *  Description:
 *  Accepts a reference to a DOM object and returns an array
 *  of all descendant elements that match a particular class
 *  name.
 *  
 *  Parameters:
 *  pRef		string or object	The parent DOM element
 *									to search
 *  pClassName	string				The class name to match
 *  
 *  Return:
 *  array		An array of DOM objects with the matching
 *				class name.
 *-------------------------------------------------------*/

function getDescendantsByClassName(pRef, pClassName) {
	var descendants = getObject(pRef).getElementsByTagName('*');

	return filterByClassName(descendants, pClassName);
}

/*--------------------------------------------------------
 *  Function:  compareById
 *  
 *  Description:
 *  Compares two DOM elements by ID.  Uses standard string
 *  comparison against language character set numeric codes.
 *  
 *  Parameters:
 *  pObjectA	object	The first DOM element to compare
 *  pObjectB	object	The second DOM element to compare
 *  
 *  Return:
 *  integer		-1 if pObjectB is greater than pObjectA,
 *				0 if equal, +1 if pObjectA is greater
 *-------------------------------------------------------*/

function compareById(pObjectA, pObjectB) {
	if (pObjectA.id > pObjectB.id) {
		return 1;
	} else if (pObjectA.id < pObjectB.id) {
		return -1;
	} else {
		return 0;
	}
}

/*--------------------------------------------------------
 *  Function:  createElement
 *  
 *  Description:
 *  Utility function for dynamic generation of DOM
 *  elements.  Sets attributes on the element based on a
 *  hash of attribute names and values.
 *
 *	Example:
 *		var newElement = createElement('a',
 *		{
 *			'class': 'myClass',
 *			'href': 'http://www.mydomain.com',
 *			'onmouseover': 'jsFunction();'
 *		});
 *  
 *  Parameters:
 *  pElement	string		DOM element to create
 *							(e.g. 'div', 'a', 'h1')
 *	pAttributes	hash		Hash of attributes and their
 *							values.
 *  
 *  Return:
 *  object		The newly created element
 *-------------------------------------------------------*/

function createElement(pElement, pAttributes) {
	// Make the element
	var newElement = document.createElement(pElement);
	
	// Set the attributes
	for (var attr in pAttributes) {
		switch (attr) {
			/*
				IE uses 'className' for setAttribute and getAttribute.
				All other browsers use 'class'.
				Setting the className property is cross-browser
			*/
			case 'class':
			case 'className':
				newElement.className = pAttributes[attr];
				break;
			/*
				setAttribute works for event handlers on Firefox, but not
				on IE.  Explicitly set the event handlers as functions
				using new Function constructor.
			*/
			case 'onblur':
				newElement.onblur = new Function(pAttributes[attr]);
				break;
			case 'onclick':
				newElement.onclick = new Function(pAttributes[attr]);
				break;
			case 'ondblclick':
				newElement.ondblclick = new Function(pAttributes[attr]);
				break;
			case 'onfocus':
				newElement.onfocus = new Function(pAttributes[attr]);
				break;
			case 'onkeydown':
				newElement.onkeydown = new Function(pAttributes[attr]);
				break;
			case 'onkeypress':
				newElement.onkeypress = new Function(pAttributes[attr]);
				break;
			case 'onkeyup':
				newElement.onkeyup = new Function(pAttributes[attr]);
				break;
			case 'onmousedown':
				newElement.onmousedown = new Function(pAttributes[attr]);
				break;
			case 'onmousemove':
				newElement.onmousemove = new Function(pAttributes[attr]);
				break;
			case 'onmouseout':
				newElement.onmouseout = new Function(pAttributes[attr]);
				break;
			case 'onmouseover':
				newElement.onmouseover = new Function(pAttributes[attr]);
				break;
			case 'onmouseup':
				newelement.onmouseup = new Function(pAttributes[attr]);
				break;
			case 'onresize':
				newElement.onresize = new Function(pAttributes[attr]);
				break;

			/* Default action is to set the attribute */
			default:
				newElement.setAttribute(attr, pAttributes[attr]);
				break;
		}
	}
	return newElement;
}

/*--------------------------------------------------------
 *  Function:  getWindowWidth
 *  
 *  Description:
 *  Cross browser method for determining total width of
 *	browser window (inside chrome)
 *  
 *  Parameters:
 *  None
 *  
 *  Return:
 *  integer		Width of browser window in pixels
 *-------------------------------------------------------*/

function getWindowWidth() {
	if (isNaN(window.innerWidth)) {
		return document.body.parentElement.clientWidth;
	} else {
		return window.innerWidth;
	}
}

/*--------------------------------------------------------
 *  Function:  getWindowHeight
 *  
 *  Description:
 *  Cross browser method for determining total height of
 *	browser window (inside chrome)
 *  
 *  Parameters:
 *  None
 *  
 *  Return:
 *  integer		Height of browser window in pixels
 *-------------------------------------------------------*/

function getWindowHeight() {
	if (isNaN(window.innerHeight)) {
		return document.body.parentElement.clientHeight;
	} else {
		return window.innerHeight;
	}
}

/*--------------------------------------------------------
 *  Function:  getScrollX
 *  
 *  Description:
 *  Cross browser method for determining horizontal scroll of
 *  browser window
 *  
 *  Parameters:
 *  None
 *  
 *  Return:
 *  integer		Number of pixels scrolled horizontally
 *-------------------------------------------------------*/

function getScrollX() {
	if (isNaN(window.scrollX)) {
		// IE compatibility mode
		return document.body.parentElement.scrollLeft;
	} else {
		return window.scrollX;
	}
}

/*--------------------------------------------------------
 *  Function:  getScrollY
 *  
 *  Description:
 *  Cross browser method for determining vertical scroll of
 *  browser window
 *  
 *  Parameters:
 *  None
 *  
 *  Return:
 *  integer		Number of pixels scrolled vertically
 *-------------------------------------------------------*/

function getScrollY() {
	if (isNaN(window.scrollY)) {
		// IE compatibility mode
		return document.body.parentElement.scrollTop;
	} else {
		return window.scrollY;
	}
}

/*--------------------------------------------------------
 *	Function:  tmpAlert
 *	
 *	Description:
 *	Temporary function for alert of server side functionality
 *
 *	Paramaters:
 *	pType		string		Verbiage for alert box
 *
 *	Return:
 *	none
 *------------------------------------------------------*/
 
function tmpAlert(pType) {
	alert('This will perform a ' + pType + '.');
	return false;
}

/*--------------------------------------------------------
 *	Function:  clearFld
 *	
 *	Description:
 *	Clears default text in form field on focus
 *
 *	Paramaters:
 *	pFld		string or object	Reference to the field to clear.
 *
 *	Return:
 *	none
 *------------------------------------------------------*/
 
function clearFld(pFld) {
	getObject(pFld).value = "";
	return false;
}

/*--------------------------------------------------------
 *	Function:  changeClass
 *	
 *	Description:
 *	Changes the class of an element.  Replaces pOldClass
 *  with pNewClass.  If pOldClass does not exist, pNewClass
 *  is still added.
 *
 *	Parameters:
 *	pRef		string or object	Reference to element that
 *									changes class
 *	pOldClass	string		Old class to replace
 *	pNewClass	string		New class to add
 *
 *	Return:
 *	none
 *------------------------------------------------------*/
 
function changeClass(pRef, pOldClass, pNewClass) {
	var obj = getObject(pRef);
	
	var newClassStr = pNewClass;
	
	if (obj.className) {
		var classNames = obj.className.split(' ');
		for (var i = 0; i < classNames.length; i++) {
			if (classNames[i] != pOldClass) {
				newClassStr += ' ' + classNames[i];
			}
		}
	}
	
	obj.className = newClassStr;
}


/*--------------------------------------------------------
 *	Function:  popWin
 *------------------------------------------------------*/

function popWin(width,height,path) {
	var features = "height="+height+",width="+width;
	features = features+",menubar=no,location=no,scrollbars=no,status=no,titlebar=no,toolbar=no,resizeable=yes";
	var w = window.open(path,"popWin",features,false);
	return false;
}


/*--------------------------------------------------------
 * Utility Exceptions
 *------------------------------------------------------*/

/*--------------------------------------------------------
 *	Exception:  ObjectNotFoundException
 *	
 *	Description:
 *	Runtime exception. Indicates that an object reference
 *	was not found.
 *
 *	Parameters:
 *	pRef		string		Erroneous reference to object
 *------------------------------------------------------*/
 
function ObjectNotFoundException(pRef) {
   this.ref = pRef;

   this.toString = function() {
      return "Object not found: " + pRef;
   };
}

/*--------------------------------------------------------
 *	Exception:  InvalidObjectRefException
 *	
 *	Description:
 *	Runtime exception. Indicates an invalid reference
 *	to an object (not a string or object reference) was used.
 *
 *	Parameters:
 *	pRef		not string or object	Erroneous reference to object
 *------------------------------------------------------*/
 
function InvalidObjectRefException(pRef) {
	this.ref = pRef;
	
	this.toString = function() {
		return "Invalid object reference: " + pRef;
	};
}

/*--------------------------------------------------------
 *	Application Specific Functions
 *------------------------------------------------------*/

/*--------------------------------------------------------
 *  Function:  showImage
 *  
 *  Description:
 *  Changes the src and alt for an image object.
 *  
 *  Parameters:
 *	pImg		string or object	Reference to the image object.
 *	pSrc		string	The new src for the image.
 *	pAlt		string	The new alt for the image.
 *  
 *  Return:
 *  none
 *-------------------------------------------------------*/

function showImage(pImg, pSrc, pAlt) {
	var imageObj = getObject(pImg);
	imageObj.src = pSrc;
	imageObj.alt = pAlt;
}

/*--------------------------------------------------------
 *	Function:  toggleCommentsDisplay
 *	
 *	Description:
 *	Changes the display of ancillary article elements
 *	(comments and post a comment)
 *
 *	Paramaters:
 *	pArea		string or object	Reference to area being toggled
 *
 *	Return:
 *	none
 *------------------------------------------------------*/
 
function toggleCommentsDisplay(pArea) {
	// Check current display
	var display = getObject(pArea).style.display;
	
	// Not showing.  Display it.
	if (display == 'none') {
		getObject(pArea).style.display = 'block';
		
		// Toggle controls
		getObject(pArea + 'LinkOn').style.display = 'none';
		getObject(pArea + 'LinkOff').style.display = 'inline';
		if ('post' == pArea) {
			getObject("forum_comment").focus();
		}

	// Showing.  Hide it.
	} else {
		getObject(pArea).style.display = 'none';
		
		// Toggle controls
		getObject(pArea + 'LinkOff').style.display = 'none';
		getObject(pArea + 'LinkOn').style.display = 'inline';
	}
}

//same as above but bizcenter html is a little diff
function toggleCommentsDisplayBC(pArea) {
	// Check current display
	var display = getObject(pArea).style.display;
	
	// Not showing.  Display it.
	if (display == 'none') {
		getObject(pArea).style.display = 'block';
		
		//if user not signed in textarea doesn't exist
		try{
			getObject("forum_comment").focus();
		}catch(e){
			//noop
		}

	// Showing.  Hide it.
	} else {
		getObject(pArea).style.display = 'none';
		
	}
}


/*--------------------------------------------------------
 *  Function:  checkAllChecks
 *  
 *  Description:
 *  Manages checked status of a series of checkboxes
 *  including a "check all" control.
 *  
 *  Parameters:
 *	pRef		string or object	Reference to checkbox that changes.
 *	pAll		string or object	Reference to the "check all" checkbox.
 *	pChecks		array	Array of references to checkbox series.
 *  
 *  Return:
 *  none
 *-------------------------------------------------------*/

function checkAllChecks(pRef, pAll, pChecks) {
	var changed = getObject(pRef);
	var all = getObject(pAll);
		
	// User changed the all checkbox
	if (changed == all) {
		// Set all of the checkboxes accordingly
		for (var i = 0; i < pChecks.length; i++) {
			getObject(pChecks[i]).checked = changed.checked;
		}
	} else {
		var allChecked = true;
		// Check to see if all checks are checked
		for (var i = 0; i < pChecks.length; i++) {
			if (!getObject(pChecks[i]).checked) {
				allChecked = false;
				break;
			}
		}
		// Only check the all checkbox if all checkboxes haved been checked
		all.checked = allChecked;
	}
}

/*--------------------------------------------------------
 *  Function:  charCounter
 *  
 *  Description:
 *  Counts the number of characters entered in input field
 *  and updates display of that number.
 *
 *	pText contains the string to display the number.
 *  It should contain the string "{CHAR}" indicating
 *	where the character count should be displayed.
 *  
 *  Parameters:
 *	pInput		string or object	Reference to input field
 *	pOutput		string or object	Reference to output display
 *	pText		string		Message text for display
 *	pMax		integer		Maximum number of characters allowed
 *  
 *  Return:
 *  none
 *-------------------------------------------------------*/

function charCounter(pInput, pOutput, pText, pMax) {
	var inputObj = getObject(pInput);
	var outputObj = getObject(pOutput);
	var length = inputObj.value.length;

	// Ensure we are not over the max
	if (length >= pMax) {
		length = pMax;
		inputObj.value = inputObj.value.substr(0, pMax);
	}

	// Display the character count
	outputObj.innerHTML = pText.replace('{CHAR}', length);
}

/*
Review Finder form
*/

function rfCatSelect(typid) {
	
	var params = new Object();
	params.id = typid;
	$.get("/product/rffilters.html",params);
}
function rfCatClose() {
	$('#rfFields').hide();
	$('#rfControls').hide();
	document.rfForm.reset();
	return false;
}

function displaySelectedSelect(selectList,id) {
	for (var i=0; i< selectList.options.length; i++) {
		if (selectList.options[i].value == id) {
			selectList.selectedIndex = i;
			break;
		}
	}
	//var selectList = document
	//var selectedModelId = select.options[select.selectedIndex].value;
}

function rfSubmit() {
	var isValid = true;
	document.rfForm.submit();
	return false;
}


/* ----------------------------------------------------
***	TEXT UTILITIES
***---------------------------------------------------- */


function LTrim( value ) {
	var re = /\s*((\S+\s*)*)/;
	return value.replace(re, "$1");
}

/* ---------------------------------------------------- */

function RTrim( value ) {
	var re = /((\s*\S+)*)\s*/;
	return value.replace(re, "$1");
}

/* ---------------------------------------------------- */

function trim( value ) {
	return LTrim(RTrim(value));
}


/* ---------------------------------------------------- */

function pad (pNum) {
	if (pNum < 10) {
		return "0" + pNum;
	} else {
		return pNum;
	}
}


/*--------------------------------------------------------
 *	Validation
 *------------------------------------------------------*/

function isNumber (val) {
	if (isNaN(parseInt(val))) {
		return false;
	}
	return true;
}



/*--------------------------------------------------------
 *	Miscellaneous Functions
 *------------------------------------------------------*/

/* SEARCH */
function search_onsubmit(){
	preSearchSubmit();
	return true;
}

function submitSearch(frmId){
	var frm = document.getElementById(frmId);
	if (frm.qt.value != 'Search PC World') {
		preSearchSubmit(frm);
		frm.submit();
	}
}

function preSearchSubmit(frm){
	if(null != frm.sw && frm.sw.checked==false) frm.old_qt.value="";
}

/* COOKIES */
function pcw_setCookie(name, value, expires, domain){
	pcw_setRawCookie(name, escape(value), expires, domain);
}

function pcw_setRawCookie(name, value, expires, domain){
	if(navigator.cookieEnabled){
		document.cookie = name+"="+value+";expires="+expires.toGMTString()+";domain="+domain+";path=/";
	}
}

function pcw_writeCookie(name, value, expires, domain){
	pcw_setCookie(name, value, expires, domain);
}

function pcw_readCookie(name){
	return unescape(pcw_readRawCookie(name));
}

function pcw_readRawCookie(name){
	if(navigator.cookieEnabled&&document.cookie!=''){
		var strAll = document.cookie;
		var i1 = strAll.indexOf(name);
		if(i1!=-1){
			// skip name and '='
			i1 = i1+name.length+1;
			i2 = strAll.indexOf(';', i1);
			if(i2==-1) i2 = strAll.length;
			return strAll.substring(i1, i2);
		}
	}
	return "";
}

function pcw_removeCookie(name, domain){
	if(navigator.cookieEnabled){
		var d = new Date();
		d.setDate(d.getDate()-30);
		document.cookie=name+"=;expires="+d.toGMTString()+";domain="+domain+";path=/";
	}
}



var ord = pcw_GetOrd(8);

//generates random number of length integers
function pcw_GetOrd (length) {
	var ord = "";
	for(var o=0;o<length;o++) {
		ord = ord + Math.floor((Math.random()*10));
	}
	ord = ord + "?";
	return ord;
}

function ar(u,i) {
	var unit= 'ad'+u;
	ms = i*1000;
	var f = function() {
		ref(unit);
	};
	setInterval(f,ms);
}

function ref(u) {
	var oldsrc=document.getElementById(u).src;
	var newsrc = oldsrc.replace(/ord=[0-9]+\?/,'ord='+pcw_GetOrd(8));
	document.getElementById(u).src = newsrc;
}

/*--------------------------------------------------------
 *  Overture ads
 *
 *  both of the Ysm functions require the existence of two variables set in code
 * 		YsmAds = an initialized array to contain ysm results
 * 		skip = number of ads to skip. will compare against index of YsmAds 
 *-------------------------------------------------------*/

function renderYsmAds (max,start) {
	var out = "";
	if (YsmAds.length > start) {
		out += "<div class='textAds'><strong>Sponsored Links</strong>";
		out += "<a href=\"javascript:void(0)\" onclick=\"popWin(300,180,'/sponsorlinkslegend')\" class='about'>About</a>";
		out += "<ul>";
	}
	for (var i=start;i<max+start;i++) {
		if (YsmAds.length > i) {
			out += renderYsmAd(i);
		} else {
			break;
		}
		skip++;
	}
	if (YsmAds.length > start) {
		out += "</ul><div class=\"spacer\"></div></div>";
	}
	//cycle back to beginning of loop
	if (skip == YsmAds.length) {
		skip = 0;
	}
	document.write(out);
}

function renderYsmAd (index) {
	var Ad = YsmAds[index];
	var out = "<li>";
	out += "<a href='"+Ad.url+"' target='_blank'>"+Ad.title+"</a>"+Ad.description;
	out += "<a href='"+Ad.url+"' class='siteLink'>"+Ad.displayUrl+"</a>";
	out += "</li>";
	return out;
}

/* am lazy. copying above rather than extending it. should merge eventually - aj */

function renderYSMBigBoxAds (id,sequence) {
	var out = "";
	for (var i=0;i<YsmAds.length;i++) {
			if (sequence == 0 && i % 2 != 0) {
				out += renderYsmBigBoxAd(i);
			} else if (sequence == 1 && i % 2 == 0) {
				out += renderYsmBigBoxAd(i);
			}
	}
    if (id != null) {
    	document.getElementById(id).innerHTML = out;
    }
}

function renderYsmBigBoxAd (index) {
	var Ad = YsmAds[index];
	var out = "<p>";
	out += "<a href='"+Ad.url+"' style='font-size:13px;font-weight:bold' target='_blank'>"+Ad.title+"</a><br/>";
	out += Ad.description+"<br/>";
	out += "<a href='"+Ad.url+"'>"+Ad.displayUrl+"</a>";
	out += "</p>";
	return out;
}


/* HITBOX */
function setHbxVals () {

	var hcDim = "|" + hbx.mlc + "/" + hbx.pn;

	var sg = new Object();
	sg.dlBrowsers = 1; //done
	sg.dlLoaders = 2;
	sg.newsUsers = 3; //done
	sg.reviewsUsers = 4; //done
	sg.blogUsers = 5; //done
	sg.idgnsUsers = 6; //done
	sg.members = 7; //done
	sg.forumViewers = 8; //done
	sg.forumPosters = 9;
	sg.ddUsers = 10; //done
	sg.bgUsers = 11; //done
	sg.pgUsers = 12; //done
	sg.rssUsers = 13; //done
	sg.icUsers = 14; //done
	sg.nlSubscribers = 15; //done
	sg.magSubscribers = 16;
	sg.pi = 17;
	sg.sem = 18; //done
	
	var sgs = new Array();
	
	var nv = true;
	var ref = document.referrer;
	if (ref.indexOf("pcworld") > -1) {nv = false;}
	
	if (hbx.mlc.indexOf("downloads") > -1 && nv == true) {sgs.push(sg.dlBrowsers);}
	if (hbx.mlc.indexOf("news") > -1 && nv == true) {sgs.push(sg.newsUsers);}
	if (hbx.mlc.indexOf("review") > -1 && nv == true) {sgs.push(sg.reviewsUsers);}
	if (hbx.mlc.indexOf("blog") > -1 && nv == true) {sgs.push(sg.blogUsers);}
	if (hbx.mlc.indexOf("idgns") > -1 && nv == true) {sgs.push(sg.idgnsUsers);}
	if (hbx.mlc.indexOf("duo") > -1 && nv == true) {sgs.push(sg.ddUsers);}
	if (hbx.mlc.indexOf("guide") > -1 && nv == true) {sgs.push(sg.bgUsers);}
	if (hbx.mlc.indexOf("/ic/") > -1 && nv == true) {sgs.push(sg.icUsers);}
	if (hbx.mlc.indexOf("forum") > -1 && nv == true) {sgs.push(sg.forumUsers);}
	if (hbx.mlc.indexOf("prices") > -1 && nv == true) {sgs.push(sg.pgUsers);}
	
	//members
	if (pcw_readCookie('userEmail') != '') {sgs.push(sg.members);}

	//tk vals
	var tk = getQsVal("tk");
	if (tk != "") {
		hbx.hc1 = tk+hcDim;
		if (tk.indexOf("nl_") > -1) {sgs.push(sg.nlSubscribers);}
		if (tk.indexOf("pcw_") > -1) {sgs.push(sg.sem);}
		if (tk.indexOf("rss_") > -1) {sgs.push(sg.rssUsers);}
	}
	
	//set appropriate hbx vals
	hbx.seg = sgs.join(",");
	
}

function getQsVal (name) {
	if (window.location.search != "") {
		var qs = window.location.search.substring(1);
		var pairs = qs.split("&");
		for (var i=0;i<pairs.length;i++) {
			var pair = pairs[i].split("=");
			if (pair[0] == name) {
				return pair[1];
				break;
			}
		}
	}
	return "";
}

/*--------------------------------------------------------
 *	Base Classes
 *------------------------------------------------------*/


/*--------------------------------------------------------
 *	Generic UI Control Classes
 *------------------------------------------------------*/

/*--------------------------------------------------------
 *	Module Specific Controls 
 *------------------------------------------------------*/


 /*--------------------------------------------------------
 *  BEGIN Page preloader class
 *-------------------------------------------------------*/

/*--------------------------------------------------------
 *  Class:  Preloader
 *  
 *  Description:
 *  Class for preloading page assets.  Currently supports
 *  preloading of images only.
 *
 *	Properties:
 *	- images: array of images to preload.
 *
 *  Methods:
 *	- intialize: Constructor.  Preloads any image sources
 *      passed in as an array of strings.
 *	- preload: Preloads an image source.
 *	- onloadHandler: Event handler for image onload events  
 *	- onerrorHandler: Event handler for image onerror events  
 *	- onabortHandler: Event handler for image onabort events  
 *  
 *  Exceptions:
 *	none
 *-------------------------------------------------------*/

//var Preloader = Class.create();
//var Preloader = new Object();

function Preloader() {
}

Preloader.prototype = {

	/*--------------------------------------------------------
	 *  Method:  initialize
	 *  
	 *  Description:
	 *  Constructor. Preloads an array of image sources.
	 *  Establishes images array for tracking preloaded images.
	 *
	 *  Parameters:
	 *  pImgArray	Array	An array string of image sources.
	 *  
	 *  Return:
	 *  none
	 *-------------------------------------------------------*/

	initialize: function(pImgArray) {
		this.images = new Array;
		for (var i = 0; i < pImgArray.length; i++) {
			this.preload(pImgArray[i]);
		}
	},

	/*--------------------------------------------------------
	 *  Method:  preload
	 *  
	 *  Description:
	 *  Preloads an image.  Adds the preloaded image to the 
	 *  images member array.  Assigns event handlers for
	 *  onerror, onload, and onabort.
	 *
	 *  Parameters:
	 *  pImg	String	String indicating the source URI for
	 *					the image to preload.
	 *  
	 *  Return:
	 *  none
	 *-------------------------------------------------------*/
	
	preload: function(pImg) {
		var img = new Image;
		img.onload = this.onloadHandler;
		img.onerror = this.onerrorHandler;
		img.onabort = this.onabortHandler;
		
		img.src = pImg;
		this.images.push(img);
	},

	/*--------------------------------------------------------
	 *  Method:  onloadHandler
	 *  
	 *  Description:
	 *  Placeholder event handler for image onload event.
	 *
	 *  Parameters:
	 *  none
	 *  
	 *  Return:
	 *  none
	 *-------------------------------------------------------*/
	
	onloadHandler: function() {
		// alert(this.src + ' loaded!');
	},

	/*--------------------------------------------------------
	 *  Method:  onerrorHandler
	 *  
	 *  Description:
	 *  Placeholder event handler for image onerror event.
	 *
	 *  Parameters:
	 *  none
	 *  
	 *  Return:
	 *  none
	 *-------------------------------------------------------*/
	
	onerrorHandler: function() {
		// alert(this.src + ' failed in error.');
	},

	/*--------------------------------------------------------
	 *  Method:  onabortHandler
	 *  
	 *  Description:
	 *  Placeholder event handler for image onabort event.
	 *
	 *  Parameters:
	 *  none
	 *  
	 *  Return:
	 *  none
	 *-------------------------------------------------------*/
	
	onabortHandler: function() {
		// alert(this.src + ' aborted.');
	}
}

/*--------------------------------------------------------
 *  END Page preloader class
 *-------------------------------------------------------*/


/*--------------------------------------------------------
 *  BEGIN FeatureViewer class
 *-------------------------------------------------------*/
 
//var FeatureViewer = Class.create();

//var FeatureViewer = new Object();

function FeatureViewer() {
	this.initialize();
}

FeatureViewer.prototype = {

	initialize: function() {

		//get and cache dom references
		this.containerNode = document.getElementById("FVContainer");
		this.contentNode = document.getElementById("FVContent");
		this.navNode = document.getElementById("FVNav");
		//this.readBtn = document.getElementById("FVReadBtn");
		this.arrNavs = this.navNode.getElementsByTagName("a");
		
		//build shadow node
		this.shadowNode = this.contentNode.cloneNode(1);
		this.shadowNode.id = "FVContentShadow";
		this.containerNode.appendChild(this.shadowNode);

		this.arrContent = this.contentNode.childNodes;
		this.arrContentShadow = this.shadowNode.childNodes;

		//set input arrays
		this.arrImages = new Array();
		this.arrLinks = new Array();
		
		//cache these values so we don't have to calculate later
		this.contentLength = 0;
		this.selectedItem = 0;

		//set timer durations		
		this.rotateDuration = 4000;
		this.idleDuration = 5000;
		this.navCloseDuration = 10000;
		
	},
	
	start: function() {

		this.contentLength = this.arrLinks.length;

		//preload images
		for (var i=0;i<this.contentLength;i++) {
			this.preload(this.arrImages[i]);
		}
		
		this.swapItem(1);
		this.startIdle();

	},
	
	swapItem: function(item) {
	
	 	if (!item) {
	 		//this is an automated swap
	 		if (this.selectedItem == this.contentLength) {
	 			item = 1;
	 		} else {
	 			item = this.selectedItem+1;
	 		}
	 	} else {
	 		this.stopAllTimers();
	 	}
	 	
	 	this.selectedItem = item;
	 	var index = item-1;

	 	
	 	this.renderContent(index);
	 	this.highlightNav(index);
	 	
	},
	
	stopAllTimers: function() {
		this.stopDelayedNavClose();
		this.stopIdle();
		this.stopRotation();
	},
	
	startIdle: function() {
		this.stopDelayedNavClose();
		this.stopIdle();
		this.stopRotation();
		this.idleInterval = setInterval("fv.startRotation()",this.idleDuration);
	},
	
	stopIdle: function() {
		clearInterval(this.idleInterval);
	},
	
	startRotation: function() {
		this.stopIdle();
		this.startDelayedNavClose();
		this.rotateInterval = setInterval("fv.swapItem()",this.rotateDuration);
	},
	
	stopRotation: function() {
		clearInterval(this.rotateInterval);
	},
	
	startDelayedNavClose: function () {
		this.stopDelayedNavClose();
		this.navCloseInterval = setInterval("fv.closeNav()",this.navCloseDuration);
	},
	
	stopDelayedNavClose: function() {
		clearInterval(this.navCloseInterval);
	},

	openNav: function() {
		fv.navNode.style.width = "188px";

		//clear existing event if it exists
		if ( fv.navNode.detachEvent ) {
			fv.navNode.detachEvent( "onmouseover", fv.openNav );
		} else {
			fv.navNode.removeEventListener( "mouseover", fv.openNav, false );
		}

		//set an event to close nav onmouseout
		if ( fv.navNode.attachEvent ) {
			fv.navNode.attachEvent( "onmouseout", fv.closeNav );
		} else {
			fv.navNode.addEventListener( "mouseout", fv.closeNav, false );
		}

		//addEvent(fooNode,"mouseout",fv.closeNav);
		//fv.startDelayedNavClose();
	},

	closeNav: function() {

		fv.stopDelayedNavClose();
		fv.navNode.style.width = "33px";

		//clear existing event
		if ( fv.navNode.detachEvent ) {
			fv.navNode.detachEvent( "onmouseout", fv.closeNav );
		} else {
			fv.navNode.removeEventListener( "mouseout", fv.closeNav, false );
		}

		//set an event to open nav onmouseover
		if ( fv.navNode.attachEvent ) {
			fv.navNode.attachEvent( "onmouseover", fv.openNav );
		} else {
			fv.navNode.addEventListener( "mouseover", fv.openNav, false );
		}

		//this.resizeNav(160,18,10);
	},
	
	resizeNav: function(start,end,interval) {
		this.navNode.style.width = start + "px";
		var newStart = start - 20;
		if (newStart > end) {
			setTimeout("fv.resizeNav("+newStart+","+end+")",interval);
		}
	},
	
	contentLink: function() {
		document.location.href = fv.arrLinks[fv.selectedItem-1];
	},

	renderContent: function(index) {
		
		//clear content
	 	for (var i=0;i<this.contentLength;i++) {
	 		this.arrContent[i].style.display = "none";
	 		this.arrContentShadow[i].style.display = "none";
	 	}

		//show content
	 	this.arrContent[index].style.display = "block";
	 	this.arrContentShadow[index].style.display = "block";
	 	this.containerNode.style.backgroundImage = "url("+this.arrImages[index]+")";

	 	//to be safe, remove old link event
		if ( fv.contentNode.detachEvent ) {
			fv.contentNode.detachEvent( "onclick", fv.contentLink );
		} else {
			fv.contentNode.removeEventListener( "click", fv.contentLink, false );
		}

	 	//add content hotspot
		if ( fv.contentNode.attachEvent ) {
			fv.contentNode.attachEvent( "onclick", fv.contentLink );
		} else {
			fv.contentNode.addEventListener( "click", fv.contentLink, false );
		}

	 	//this.readBtn.setAttribute("href",this.arrLinks[index]);

	},
	
	highlightNav: function(index) {
	 	for (var i=0;i<this.arrContent.length;i++) {
	 		this.arrNavs[i].className = "";
	 	}
	 	this.arrNavs[index].className = "FVNavOn";
	},
	
	preload: function(url) {
		var img = new Image;
		img.src = url;
		//this.arrImages.push(url);
	}

}

/*--------------------------------------------------------
 *  END FeatureViewer class
 *-------------------------------------------------------*/

function downgradeWin1252Chars(sIn){
	var arrCharMap = {
		128:"&#8364;", /* euro */
		133:"...", /* ellipsis */
		145:"'",
		146:"'",
		147:'"',
		148:'"',
		150:"-",
		151:"-"
	}
	
	var sOut = sIn;

	for(var i = 0; i< sOut.length; i++){
		if(sOut.charCodeAt(i) > 127) {
			var charCode = sOut.charCodeAt(i);
			var replacement = arrCharMap[charCode] ? arrCharMap[charCode] : "&#"+charCode+";";
			sOut = sOut.substr(0,i) + replacement + sOut.substr(i+1);
			i = i + replacement.length;
		}
	}

	//dbg:alert(sIn+'\n\n'+sOut);
	return sOut;
}
 
/*--------------------------------------------------------
 *  Class:  ArticleComments
 *  
 *  Description:
 *	Fetches/Posts/Displays comments in cached article pages
 *
 *  Properties
 *  - threadId (required): thread id associated with this article
 *  - communityId (required): community id associated with comments
 * 
 *  Methods:
 *	- getComments: Gets current comments
 *	- postComment: posts a user comment to server
 *	- injectComments: takes ajax result from either of above, inserts
 *		comment data into page
 *  
 *-------------------------------------------------------*/

var ArticleComments = new Object();
ArticleComments.threadId = null;
ArticleComments.communityId = null;
ArticleComments.isLoggedOn = 0;
ArticleComments.style = "default";

/* call with threadId */
ArticleComments.getComments = function() {
	if (null==ArticleComments.threadId || null==ArticleComments.communityId) {
		return false;
	}
	if (null != Logon.isValid) {
		ArticleComments.isLoggedOn = 1;
	}
	$.get("/articleComment/get.chtml",
		{
			threadId:		ArticleComments.threadId,
			style:			ArticleComments.style
		}
	);
}

ArticleComments.postComment = function() {
	//alert("article commenting has been disabled during alpha testing");
	//return false;
	
	if (null==ArticleComments.threadId || null==ArticleComments.communityId) {
		return false;
	}
	if (null != Logon.isValid) {
		ArticleComments.isLoggedOn = 1;
	}
	var commentNode = document.getElementById('forum_comment');

	var commentText = commentNode.value;
	if ('' == commentText) {
		alert('Please enter a comment');
		commentNode.focus();
		return;
	}
	/* Clear post text-area */
	commentNode.value = '';
	document.getElementById('postingMessage').style.display = 'block';
	$.post("/articleComment/post.do",
		{
			threadId:		ArticleComments.threadId,
			communityId:	ArticleComments.communityId,
			style:			ArticleComments.style,
			postSubject:	downgradeWin1252Chars(ArticleComments.subject),
			postComment:	downgradeWin1252Chars(commentText)
		}
	);
}

/*--------------------------------------------------------
 *  article Voting
 *-------------------------------------------------------*/

var ArticleVote = new Object();
ArticleVote.aid = null;
ArticleVote.catid = null;
ArticleVote.dspid = null;
ArticleVote.message = null;
ArticleVote.myVote = null;
ArticleVote.style = "default";

ArticleVote.getVotes = function() {
	//Check for existing vote
	var cookie = pcw_readCookie('articleVotes');
	var strAid = String(ArticleVote.aid);
	var idx = cookie.indexOf(strAid);

	if (idx > -1) {
		var end = cookie.indexOf('\n',idx);
		if(end == -1) end = cookie.length;
		ArticleVote.myVote = cookie.substring(idx+strAid.length+1,end);
	}
	
	var params = new Object();
	params.aid = ArticleVote.aid;
	params.style = ArticleVote.style;
	if (ArticleVote.myVote) {
		params.hasVoted = true;
		params.vote = ArticleVote.myVote;
	}
	
	if (ArticleVote.catid) {
		params.catid = ArticleVote.catid;
	}
	
	if (ArticleVote.dspid) {
		params.dspid = ArticleVote.dspid;
	}
	
	if (ArticleVote.message) {
		params.message = ArticleVote.message;
	}
	
	//get votes
	$.get("/articleVote/get.chtml", params);
}

ArticleVote.submitVote = function(vote) {

	//handle vote
	if (vote == "no") {
		ArticleVote.myVote = "no";
	} else {
		ArticleVote.myVote = "yes";
	}
	
	//set voted cookie
	var d = new Date();
	d.setHours(d.getHours()+24);
	var cookie = pcw_readCookie('articleVotes');
	cookie = cookie + '\n'+ArticleVote.aid+'\t'+ArticleVote.myVote;
	pcw_setCookie('articleVotes', cookie, d, 'pcworld.com');

	var params = new Object();
	params.aid = ArticleVote.aid;
	params.style = ArticleVote.style;
	params.vote = ArticleVote.myVote;
	params.nocache = Math.round((Math.random() * 90000) + 1);

	//submit vote
	$.post("/articleVote/post.do",params,reload);

}

/*
Reload page on 
*/
function reload () {
	window.location.reload(true);
}

/*--------------------------------------------------------
 *  download Voting
 *-------------------------------------------------------*/

var DownloadVote = new Object();
DownloadVote.fid = null;
DownloadVote.myVote = null;

DownloadVote.getVotes = function() {

	//Check for existing vote
	var cookie = pcw_readCookie('DownloadVotes');
	var strFid = String(DownloadVote.fid);
	var idx = cookie.indexOf(strFid);
	if (idx > -1) {
		var end = cookie.indexOf('\n',idx);
		if(end == -1) end = cookie.length;
		DownloadVote.myVote = cookie.substring(idx+strFid.length+1,end);
	}
	
	var params = new Object();
	params.fid = DownloadVote.fid;
	if (DownloadVote.myVote) {
		params.hasVoted = true;
		params.vote = DownloadVote.myVote;
	}
	
	//get votes
	$.get("/DownloadVote/get.chtml",params);
}

DownloadVote.submitVote = function(vote) {
	if(DownloadVote.myVote != undefined){
		return;
	}
	//handle vote
	if (vote == "no") {
		DownloadVote.myVote = "no";
	} else {
		DownloadVote.myVote = "yes";
	}
	
	//set voted cookie
	var d = new Date();
	d.setHours(d.getHours()+24);
	var cookie = pcw_readCookie('DownloadVotes');
	cookie = cookie + '\n'+DownloadVote.fid+'\t'+DownloadVote.myVote;
	pcw_setCookie('DownloadVotes', cookie, d, 'pcworld.com');

	var params = new Object();
	params.fid = DownloadVote.fid;
	params.vote = DownloadVote.myVote;
	params.nocache = Math.round((Math.random() * 90000) + 1);

	//submit vote
	$.post("/DownloadVote/post",params);

}


/*--------------------------------------------------------
 *	Function:  tabSwap
 *	
 *	Description:
 *	changes visible tab in a standard tabswap module
 *
 *	Parameters:
 *	tabsetId	string		Reference to tabswap module id
 *	node		object		selected node
 *
 *	Return:
 *	none
 *------------------------------------------------------*/

function tabSwap(tabsetId,node) {

	var selectedNode = node.parentNode;
	
	if (selectedNode.className.indexOf("Selected") > -1) {
		return false;
	}

	var contentContainer = getChildrenByClassName(tabsetId,"tabContentGroup")[0];
	var tabContainer = getChildrenByClassName(tabsetId,"tabs")[0];

	//swap tabs
	var tabNodes = tabContainer.childNodes;
	var tabCounter = 0;
	var index = 0;
	for (var i=0; i<tabNodes.length; i++) {
		if (tabNodes[i].nodeType == 1) {
			var className = tabNodes[i].className;
			if (tabNodes[i] == selectedNode) {
				tabNodes[i].className = className+"Selected";
				index = tabCounter;
			} else {
				tabNodes[i].className = className.replace("Selected", "");
			}
			tabCounter++;
		}
	}
		
	//swap content	
	var contentNodes = contentContainer.childNodes;
	var contentCounter = 0;
	for (var i=0; i<contentNodes.length; i++) {
		if (contentNodes[i].nodeType == 1) {
			if (contentCounter == index) {
				contentNodes[i].style.display = "block";
			} else {
				contentNodes[i].style.display = "none";
			}
			contentCounter++;
		}
	}
	
}

/*--------------------------------------------------------
 *  Function:  timestamp
 *  
 *  Description:
 *  Accepts a date and style, writes localized timestamp
 *  	in specified style to the reader's browser
 *  
 *  Parameters:
 *  date	string	unixtime timestamp
 *  style	string	predefined style of output
 *							
 *  Return:
 *  none		
 *-------------------------------------------------------*/
 
function timestamp(pDate,style) {

	var date = new Date(pDate);
	var now = new Date();

	var year = date.getFullYear();
	var month = date.getMonth()+1;
	var monthday = date.getDate();
	var weekday = date.getDay();
	var hour = date.getHours();
	var minute = pad(date.getMinutes());

	var strShortMonth = "";	
	var strLongMonth = "";
	switch (month) {
		case 1:
			strShortMonth = "Jan";
			strLongMonth = "January";
			break;
		case 2:
			strShortMonth = "Feb";
			strLongMonth = "February";
			break;
		case 3:
			strShortMonth = "Mar";
			strLongMonth = "March";
			break;
		case 4:
			strShortMonth = "Apr";
			strLongMonth = "April";
			break;
		case 5:
			strShortMonth = "May";
			strLongMonth = "May";
			break;
		case 6:
			strShortMonth = "Jun";
			strLongMonth = "June";
			break;
		case 7:
			strShortMonth = "Jul";
			strLongMonth = "July";
			break;
		case 8:
			strShortMonth = "Aug";
			strLongMonth = "August";
			break;
		case 9:
			strShortMonth = "Sep";
			strLongMonth = "September";
			break;
		case 10:
			strShortMonth = "Oct";
			strLongMonth = "October";
			break;
		case 11:
			strShortMonth = "Nov";
			strLongMonth = "November";
			break;
		case 12:
			strShortMonth = "Dec";
			strLongMonth = "December";
			break;
	}
	
	var strWeekday = "";
	switch (weekday) {
		case 0:
			strWeekday = "Sunday";break;
		case 1:
			strWeekday = "Monday";break;
		case 2:
			strWeekday = "Tuesday";break;
		case 3:
			strWeekday = "Wednesday";break;
		case 4:
			strWeekday = "Thursday";break;
		case 5:
			strWeekday = "Friday";break;
		case 6:
			strWeekday = "Saturday";break;
	}
	
	var meridian = "am";
	if (hour >= 12) {
		hour = hour - 12;
		meridian = "pm";
	}
	if (hour == 0) {
		hour = 12;
	}
	
	var time = hour+":"+minute+" "+meridian;
	var shortDate = strShortMonth + " " + monthday;
	var longDate = shortDate + ", " + year;
	
	var timestamp = "";
	switch (style) {
		case "longDate" :
			document.write(longDate);
			break;
		case "shortDate" :
			document.write(shortDate);
			break;
		case "time" :
			document.write(time);
			break;
		case "longDateTime" :
			document.write(longDate + " " + time);
			break;
		case "shortDateTime" :
			document.write(shortDate + ", " + time);
			break;
		case "homepage" :
			if (date.getDate() == now.getDate()) {
				document.write(time);
			} else {
				document.write(shortDate);
			}
			break;
		case "dateTime" :
			if (date.getYear() == now.getYear()) {
				document.write(shortDate + ", " + time);
			} else {
				document.write(longDate + " " + time);
			}
			break;
		default :
			//document.write(time_ago_in_words(pDate));
			if (date.getYear() == now.getYear()) {
				document.write(shortDate);
			} else {
				document.write(longDate);
			}
	}
	
}

function pad (pNum) {
	if (pNum < 10) {
		return "0" + pNum;
	} else {
		return pNum;
	}
}

function time_ago_in_words(from) {
	return distance_of_time_in_words(new Date().getTime(), from) 
}

function distance_of_time_in_words(to, from) {
	seconds_ago = ((to  - from) / 1000);
	minutes_ago = Math.floor(seconds_ago / 60)

	if(minutes_ago == 0) { return "less than a minute";}
	if(minutes_ago == 1) { return "a minute ago";}
	if(minutes_ago < 45) { return minutes_ago + " minutes ago";}
	if(minutes_ago < 90) { return " about 1 hour ago";}
	hours_ago  = Math.round(minutes_ago / 60);
	if(minutes_ago < 1440) { return "about " + hours_ago + " hours ago";}
	if(minutes_ago < 2880) { return "1 day ago";}
	days_ago  = Math.round(minutes_ago / 1440);
	if(minutes_ago < 43200) { return days_ago + " days ago";}
	if(minutes_ago < 86400) { return "about 1 month ago";}
	months_ago  = Math.round(minutes_ago / 43200);
	if(minutes_ago < 525960) { return months_ago + " months ago";}
	if(minutes_ago < 1051920) { return "about 1 year ago";}
	years_ago  = Math.round(minutes_ago / 525960);
	return "over " + years_ago + " years ago" 
}
 

/*--------------------------------------------------------
 *  getPricingUrl
 *
 *  dynamically generates pricing urls to shopping
 * 		prodid - pricegrabber masterid
 *-------------------------------------------------------*/

function getPricingUrl(prodid,sortby,filename) {
	if (filename == null) {
		filename = "pricing.html";
	}
	var zip = pcw_readCookie("pcw.shopping.zip");
	var url = "/shopping/detail/prtprdid,"+prodid;
	if (sortby != null && sortby != "") {
		if (zip != "" && sortby == "price") {
			sortby = "blprice";
		}
		url += "-sortby," + sortby;
	}
	if (zip != "") {
		url += "-zip," + zip;
	}
	url += "/" + filename;
	window.location.href = url;
}

function getPricingExitUrl(prodid) {
	var zip = pcw_readCookie("pcw.shopping.zip");
	var url = "/shopping/exit/prtprdid,"+prodid;
	if (zip != "") {
		url += "-zip," + zip;
	}
	url += "/exit.html";
	window.location.href = url;
}

/*--------------------------------------------------------
 *  ShoppingUrl onClick Handler
 *
 *  Appends zip code info to pricing urls (a.pricingRewrite)
 * 	after a click event.	
 *-------------------------------------------------------*/
var pricingZipRewriteRegEx= /shopping\/(detail|exit)\/([^/]+)\/(.*)/;

	$("a.pricingRewrite").click(function () {
    	var zip = pcw_readCookie("pcw.shopping.zip");
    	if(zip != undefined && zip.length >0){
    		var href=$(this).attr("href");
    		var matchInfo=href.match(pricingZipRewriteRegEx);
    		if(matchInfo != undefined && href.indexOf("zip")==-1){
    			var newHref="/shopping/"+matchInfo[1]+"/"+matchInfo[2]+"-zip,"+zip+"/"+matchInfo[3];
    			window.location.href=newHref;
    			return false;
    		}
    	}
    	return true;
  });
  


/*--------------------------------------------------------
 *  Inform helper
 *-------------------------------------------------------*/

function doInformUrl(path) {
	var p = path;
	p = p.replace(/^\/tags\//,"");
	p = p.replace(/\.html$/,"");
	p = p.replace(/\./g,"%2E");
	window.location.href = "/tags/" + p + ".html";
}

/*--------------------------------------------------------
 *  SMB Product Finder Nav
 *-------------------------------------------------------*/

var filterSelected = false;
function toggleFilter (index,nodeId) {
	if (filterSelected) {
		showAllFilters(nodeId);
	} else {
		showSelectedFilter(index,nodeId);
	}
	return false;
}

function showSelectedFilter (index,nodeId) {
	var kids = document.getElementById(nodeId).childNodes;
	var counter = 0;
	for (var i=0;i<kids.length;i++) {
		var kid = kids[i];
		if (kid.nodeType == 1 && kid.className != "itemMain") {
			if (index == counter) {
				kid.className = "itemSelected";
				//add "show all" link
				var selectedItemHeader = kid.getElementsByTagName("h3")[0];
				selectedItemHeader.appendChild(createToggleFilterLink(index,nodeId));
			} else {
				kid.style.display = "none";
			}
			counter++;
		}
	}
	filterSelected = true;
}

function showAllFilters (nodeId) {
	var kids = document.getElementById(nodeId).childNodes;
	for (var i=0;i<kids.length;i++) {
		var kid = kids[i];
		if (kid.nodeType == 1 && kid.className != "itemMain") {
			kid.className = "item";
			kid.style.display = "inline";
		}
	}
	
	//remove extra added link
	var showAllLink = document.getElementById("pfItemSelectedToggleLink");
	if (showAllLink != null) {
		showAllLink.parentNode.removeChild(showAllLink);
	}
	filterSelected = false;
}

function createToggleFilterLink (index,nodeId) {
	var link = document.createElement("a");
	link.setAttribute("id","pfItemSelectedToggleLink");
	link.setAttribute("class","viewAll");
	link.setAttribute("href","javascript:void(0)");
	link.setAttribute("onclick","toggleFilter("+index+",'"+nodeId+"');return false;");
	link.innerHTML = "(View All Filters)";
	return link;
}

function validatePrices(frm){
	re = /^\d*$/;
	if(re.exec(frm.lo_p.value) && re.exec(frm.hi_p.value)){
		return true;
	}
	alert("Please enter whole dollar amounts without commas or decimals.");
	return false;
}

function clearPriceRange(){
	document.priceForm.lo_p.value = "";
	document.priceForm.hi_p.value = "";
	document.priceForm.submit();
}


function swaptab(tabsetId,node) {

	var selectedNode = node.parentNode;

	var contentContainer = getChildObjectsByClassName(tabsetId,"tabContentGroup")[0];
	var tabContainer = getChildObjectsByClassName(tabsetId,"tabs")[0];

	//swap tabs
	var tabNodes = tabContainer.childNodes;
	var tabCounter = 0;
	var index = 0;
	for (var i=0; i<tabNodes.length; i++) {
		if (tabNodes[i].nodeType == 1) {
			if (tabNodes[i] == selectedNode) {
				tabNodes[i].childNodes[0].className = "selected";
				index = tabCounter;
			} else {
				tabNodes[i].childNodes[0].className = "";
				//tabNodes[i].blur();
			}
			tabCounter++;
		}
	}
		
	//swap content	
	var contentNodes = contentContainer.childNodes;
	var contentCounter = 0;
	for (var i=0; i<contentNodes.length; i++) {
		if (contentNodes[i].nodeType == 1) {
			if (contentCounter == index) {
				contentNodes[i].style.display = "block";
			} else {
				contentNodes[i].style.display = "none";
			}
			contentCounter++;
		}
	}
	
}

function getRegForm(partner,eventId,isSave) {

	//alert(partner+","+eventId+","+isSave);

	//return false;

	//var params = new Array();
	var params = new Object();

	if (null != partner) {
		params.partner = partner;
		//params.push("partner="+partner);
	}

	if (null != eventId) {
		params.eventId = eventId;
		//params.push("eventId="+eventId);
	}
	

	//take form data and save it
	if (isSave != undefined) {

		//alert("is a save");

		if (!document.on24Form.coregConfirm.checked) {
			alert("In order to launch the webcast, you must first check the box and agree to the On24 and PC World privacy policy and terms of use.");
			document.on24Form.coregConfirm.focus();
			return false;
		} else {
			params.coregConfirm = 'on';
			//params.push("coregConfirm=on");
			//alert("coregConfirm checked");
		}

		//handle various flavors
		if (null != document.on24Form.optin) {
			if (document.on24Form.optin.checked) {
				params.optin = 2;
				//params.push("optin=2");
			} else {
				params.optin = 1;
				//params.push("optin=1");
			}
		} else {
			params.optin = 0;
			//params.push("optin=0");
		}

		for (var i=0; i< document.on24Form.elements.length; i++) {
			//alert(element.name+":"+element.value);
			var element = document.on24Form.elements[i];
			if (element.name != "optin" && element.name != "coregConfirm") {
				params[element.name] = element.value;
				//params.push(element.name+"="+escape(element.value));
			}
		}

		//alert(params.join('\n'));

	}
	
	
	$.post("/businesscenter/webcast/form.do",params,showRegForm);

/*
	var myAjax = new Ajax.Request(
	    "/businesscenter/webcast/form.do",
	    {
	    	method: 'post',
	    	parameters: params.join('&'),
	    	onComplete: showRegForm
	    }
	);
	*/
	
	return false;
}


function showRegForm(req) {
	var displayNode = document.getElementById("regForm");
	displayNode.innerHTML = req;
	displayNode.style.display = "block";
}

function doFilterPrice(price,url) {
	window.location.href=url+'&prod_price='+price;
}

function randomlyShowOneItem(lst){
	var items = lst.getElementsByTagName("li");
	var iSize = items.length;
	var iRandom = Math.floor(iSize * Math.random());
	items[iRandom].style.display = 'inline';
}

$.fn.replaceEscapedHtml = function(newContentElements) {
    return this.each(function() {
        $(this).empty().append(newContentElements);
        this.innerHTML = this.innerHTML.replace(/&amp;/g,"&");
    });
};
