/*--------------------------------------------------------
 *	Utility Functions
 *------------------------------------------------------*/

/*--------------------------------------------------------
 *  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:  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
 *	
 *	Description:
 *	Opens a pop-up window
 *
 *	Parameters:
 *	width	string
 *	height	string
 *	path	string
 *
 *	Return:
 *	none
 *------------------------------------------------------*/

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);
}

/*--------------------------------------------------------
 *  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" 
}

/*--------------------------------------------------------
 * 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:  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++;
		}
	}
	
}


/*--------------------------------------------------------
 *  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;

/* 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
		}
	);
}

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,
			postSubject:	downgradeWin1252Chars(ArticleComments.subject),
			postComment:	downgradeWin1252Chars(commentText)
		}
	);
}

/*--------------------------------------------------------
 *  article Voting
 *-------------------------------------------------------*/

var ArticleVote = new Object();
ArticleVote.aid = null;
ArticleVote.myVote = null;

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;
	if (ArticleVote.myVote) {
		params.hasVoted = true;
		params.vote = ArticleVote.myVote;
	}
	
	//get votes
	$.get("/articleVote/get.chtml",params);
}

ArticleVote.submitVote = function(vote, aid, bArchive) {

	//handle vote
	if (vote == "no") {
		ArticleVote.myVote = "no";
	} else {
		ArticleVote.myVote = "yes";
	}
	
	if(aid)
		ArticleVote.aid = aid;
	
	//set voted cookie
	var d = new Date();
	d.setHours(d.getHours()+24);
	var cookie = pcw_readCookie('articleVotes');
	if(cookie && cookie.indexOf(ArticleVote.aid) > -1){
		ArticleVote.hasVoted = true;
	}else{
		ArticleVote.hasVoted = false;
		cookie = cookie + '\n'+ArticleVote.aid+'\t'+ArticleVote.myVote;
		pcw_setCookie('articleVotes', cookie, d, 'macworld.com');
	}

	var params = new Object();
	params.aid = ArticleVote.aid;
	params.vote = ArticleVote.myVote;
	if(ArticleVote.hasVoted)
		params.hasVoted = ArticleVote.hasVoted;
	if(bArchive)
		params.format = "archive";
	params.nocache = Math.round((Math.random() * 90000) + 1);

	//submit vote
	if(bArchive)
		$.post("/articleVote/post.do",params);
	else
		$.post("/articleVote/post.do",params,reload);

}

/*
Reload page on 
*/
function reload () {
	window.location.reload(true);
}

/*--------------------------------------------------------
 *  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=/";
	}
}



/*--------------------------------------------------------
 *  ads
 *-------------------------------------------------------*/

function pcw_AdInit () {
	//set global defaults
	pcw_ad_site = "pcw_general";
	pcw_ad_zone = "";
	pcw_ad_pos = "";
	pcw_ad_sec = "";
	pcw_ad_aid = "";
	pcw_ad_channels = "";
	pcw_ad_pcat = "";
	pcw_ad_cats = "";
	pcw_ad_sz = "";
	pcw_ad_tile = "";
	pcw_ad_tags = "";
	pcw_ad_ord = "";
	pcw_ad_custom = "";
	pcw_ad_width = "";
	pcw_ad_height = "";
	ad_debug = false;

	pcw_ad_ord = pcw_GetOrd(8);
	
}

//generates random number of length integers
function pcw_GetOrd (length) {
//random number not getting regenerated on soft refresh
//doubleclick recommends using timestamp for ord value
/*
	var ord = "";
	for(var o=0;o<length;o++) {
		ord = ord + Math.floor((Math.random()*10));
	}
*/
	var dNow = new Date();
	var ord = dNow.getTime();
	
	return ord;
}

function pcw_RefreshAd (adunit) {

	//refresh ord and url vals
	pcw_ad_ord = pcw_GetOrd(8);
	pcw_SetAdVals(adunit);

	if (adunit != null) {
		var url = pcw_AdDartUrl("iframe");
		if (url != "") {
			try {
				document.getElementById(adunit).src = url;
			} catch (e) {
				//do nothing
			}
		}
	}
	return false;

}

//overrides to be called on the page *after* the server-side options have been set
function pcw_AdOverride () {

	//what are some cookies that I have set
	//what is my referrer? do something with it
	
}

/*
adunit: specifies named unit (banner|tower|showcase|halfpage|button)
*/

function pcw_SetAdVals (adunit,tile) {
	switch (adunit) {
		case "leader":
			pcw_ad_width="728";
			pcw_ad_height="90";
			pcw_ad_tile="1";
			//pcw_ad_custom="";
			break;
		case "leader2":
			pcw_ad_width="728";
			pcw_ad_height="90";
			pcw_ad_tile="18";
			//pcw_ad_custom="";
			break;
		case "showcase-lg":
			pcw_ad_width="336";
			pcw_ad_height="280";
			pcw_ad_tile="2";
			//pcw_ad_custom=""
			break;
		case "showcase-lg2":
			pcw_ad_width="336";
			pcw_ad_height="280";
			pcw_ad_tile="17";
			//pcw_ad_custom=""
			break;
		case "tower-lg":
			pcw_ad_width="160";
			pcw_ad_height="600";
			pcw_ad_tile="3";
			//pcw_ad_custom=""
			break;
		case "halfpage":
			pcw_ad_width="336";
			pcw_ad_height="850";
			pcw_ad_tile="4";
			//pcw_ad_custom=""
			break;
		case "textbox":
			pcw_ad_width="160";
			pcw_ad_height="42";
			pcw_ad_tile="5";
			//pcw_ad_custom=""
			break;
			//textbox2 = textbox + tile 6
		case "button":
			pcw_ad_width="160";
			pcw_ad_height="60";
			pcw_ad_tile="7";
			//pcw_ad_custom=""
			break;
		case "directvendor":
			pcw_ad_width="2";
			pcw_ad_height="1";
			pcw_ad_tile="8";
			//pcw_ad_custom=""
			break;
		case "navbar":
			pcw_ad_width="186";
			pcw_ad_height="122";
			pcw_ad_tile="9";
			//pcw_ad_custom=""
			break;
		case "button-sm":
			pcw_ad_width="120";
			pcw_ad_height="30";
			pcw_ad_tile="10";
			//pcw_ad_custom=""
			break;
		case "interstitial":
			pcw_ad_width="1";
			pcw_ad_height="1";
			pcw_ad_tile="16";
			//pcw_ad_custom=""
			break;
		case "toolbar":
			pcw_ad_width="176";
			pcw_ad_height="31";
			pcw_ad_tile="13";
			//pcw_ad_custom="";
			break;
	}

	//add custom units here
	if (adunit.indexOf("button-lg")>-1) {
		pcw_ad_width="300";
		pcw_ad_height="100";
		pcw_ad_tile="11";
	}
	
	//override with incoming tile value
	if (tile) {
		pcw_ad_tile = tile;
	}
	
	if (pcw_ad_site == "") {
		pcw_ad_site = "mcw.mw.general";
	}

	pcw_ad_pos=adunit;
	pcw_ad_sz=pcw_ad_width+"x"+pcw_ad_height;
}


function pcw_AdRender(adtype,adunit,tile) {

	pcw_SetAdVals(adunit,tile);
	var ad = '';

	//build the ad
	if (ad_debug) {
		ad = ad + '<div style="width:'+pcw_ad_width+'px;height:'+pcw_ad_height+'px;background-color:#606;color:#ff0">';
		ad = ad + pcw_AdDartUrl(adtype);
		ad = ad + '</div>';
	} else {
		switch (adtype) {
			case "script":
				ad = '<scr'+'ipt type=\"text/javascript\" ';
				ad = ad + 'src="' + pcw_AdDartUrl("script")+ '">';
				ad = ad + '</scr'+'ipt>';
				break;
			case "iframe":
				ad = '<iframe name="'+adunit+'" id="'+adunit+'" src="' + pcw_AdDartUrl("iframe") + '" width="' + pcw_ad_width + '" height="' + pcw_ad_height + '" marginheight="0" marginwidth="0" scrolling="no" frameborder="0">';
				ad = ad + '<a href="' + pcw_AdDartUrl("click") + '" target="_top">';
				ad = ad + '<img width="' + pcw_ad_width + '" height="' + pcw_ad_height + '" src="' + pcw_AdDartUrl("image") + '" alt="Advertisement" /></a>';
				ad = ad + '</iframe>';
				break;
		}
	}

	return ad;

}

/*
unit: specifies named unit (banner|tower|showcase|halfpage|button)
type: specifies type of tag (iframe|js)
*/

function pcw_AdDartUrl(type) {

	//override for 2nd showcase or leader
	var myadsite = pcw_ad_site;
	if (pcw_ad_pos.substring(pcw_ad_pos.length-1) == "2") {
		myadsite += "2";
	}
	
	//what kind uf url to build?
	switch (type) {
		case "iframe":
			type="adi";
			break;
		case "click":
			type="jump";
			break;
		case "image":
			type="ad";
			break;
		case "script":
			type="adj";
			break;
	}

	//base url
	url = 'http://ad.doubleclick.net/'+type+'/'+myadsite+'/'+pcw_ad_zone+";";
	
	//custom attributes
	url += pcw_ad_custom

	//content attributes
	url += pcw_AdKey("sec",pcw_ad_sec) + pcw_AdCats(pcw_ad_cats) + pcw_AdKey("aid",pcw_ad_aid);
	
	if (pcw_ad_channels != "") {
		url += pcw_AdKeys("ch",pcw_ad_channels.split(","));
	}
	
	//size attributes
	url += pcw_AdKey("pos",pcw_ad_pos) + pcw_AdKey("tile",pcw_ad_tile) + pcw_AdKey("sz",pcw_ad_sz);

	//cache breaker	
	url += pcw_AdKey("ord",pcw_ad_ord);
	
	return url;

}

/*
constructs the key tags in their proper form
*/

function pcw_AdKey (key, val) {
	var strOut = "";
	if (ad_debug) {strOut += " "}
	if (val) {
		strOut+=key+"="+val+";";
	}
	
	return strOut;
}

/*
wrapper for handling arrays of key pairs with same name
*/
function pcw_AdKeys (key, aVal) {
	var strOut = "";
	for (var i=0;i<aVal.length;i++) {
		strOut += pcw_AdKey(key,aVal[i]);
	}
	return strOut;
}

/*
splits cat values and generates their key tags
*/

function pcw_AdCats (sVal) {
	var strOut = "";
	if (sVal) {
		var aVal = new Array();
		aVal = sVal.split(",");
		var ctr = 0;
		for (var i=0;i<aVal.length;i++) {
			if (aVal[i] == pcw_ad_pcat) { 
				continue;
			} else {
				if (ctr < 5) {
					strOut += pcw_AdKey("c",aVal[i]);
					ctr++;
				} else {
					break;
				}
			}
		}
	}
	if (pcw_ad_pcat != "") {
		strOut += pcw_AdKey("c",pcw_ad_pcat);
	}
	return strOut;
}


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 "";
}



/*--------------------------------------------------------
 *  analytics
 *-------------------------------------------------------*/

/*--------------------------------------------------------
 *  domain-specific
 *-------------------------------------------------------*/

function showHomepageDefault () {
	var cookie = pcw_readCookie('hpAll');
	if (cookie.length > 0) {
		tabSwap('homepageTabs',document.getElementById('as'));
		$("#toggleTarget").html("Set \"Top Stories\" as Default");
	} else {
		$("#toggleTarget").html("Set \"All Stories\" as Default");
	}
}

function toggleHomepageDefault () {
	var cookie = pcw_readCookie('hpAll');
	if (cookie.length > 0) {
		pcw_removeCookie('hpAll', 'macworld.com');
		$("#fancysearch label")
		$("#toggleTarget").html("Set \"All Stories\" as Default");
	} else {
		var d = new Date();
		d.setFullYear(d.getFullYear()+1);
		pcw_setCookie('hpAll', 'true', d, 'macworld.com');
		$("#toggleTarget").html("Set \"Top Stories\" as Default");
	}
	return false;
}

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;
}

//ugh - stupid IE6
try {document.execCommand("BackgroundImageCache?", false, true);} catch(err) {}

function goProdList(type,subtype) {
	var url = "/product/"+type+"/"+subtype+".html";
	window.location.href = url;
}

/*  
 *      Emergency fix: Convert all old userEmail cookies to new URL encoded cookies   
 *      REMOVE after 07/28/2008  
 */  
//var patchUserEmail = pcw_readRawCookie('macUserEmail');
//if (-1 < patchUserEmail.indexOf('@')) {  
//	patchUserEmail = patchUserEmail.replace(/@/g,"%40");  
//	pcw_setRawCookie('macUserEmail', patchUserEmail, new Date(new Date().getTime() + 1209600000), 'macworld.com');  
//}

function randomlyShowOneItem(lst){
	var items = lst.getElementsByTagName("li");
	var iSize = items.length;
	var iRandom = Math.floor(iSize * Math.random());
	items[iRandom].style.display = 'block';
}

function mwabbreviate(sIn, iMaxChars){
	var sOut = sIn;
	if(sIn && sIn.length > iMaxChars){
		sOut = sIn.substr(0, iMaxChars-3)+'...';
	}
	
	return sOut;
}

/*********************************
* 	Mobile OptIn
*********************************/

$(document).ready(function(){
	var optOutCookie = pcw_readCookie('mw.mobileOptOut');
	if (optOutCookie!=undefined && optOutCookie=='optout'){
		$("<div id='mobileOptIn'><a href='javascript:void(0)' onClick='mobileOptIn()'>Click here to return to the mobile version of Macworld</a>.</span>").insertBefore("#navigation");
	}
	
});
function mobileOptIn(){
		var d = new Date();
		d.setFullYear(d.getFullYear()-20);
		pcw_writeCookie("mw.mobileOptOut","",d,".macworld.com");
		location.href=location.href;
}