
var GLOBAL_THRESHOLD = 100; // threshold for everybody
var descContainer = new Array();
var changesContainer = new Array();

var prodPanePattern = /products(\d+)/;
var fixedLocationPattern = /deal\/\d+/;
var currentProductPane = null;

	/// convert 'static' index references (deal/123/etc) to relative paths
function staticLocationRelpath(loc) {
	return (window.location.pathname.match(fixedLocationPattern)) ? "/"+loc : loc;
}
	
		

function showInitial() {
	try {
		// select first pane, make active
		var mainContainer = document.getElementById("body1"); // main_content
		if (mainContainer) {
			var allDivs = mainContainer.getElementsByTagName("div");
			var prodPanes = new Array();
			for (var i=0; i<allDivs.length; i++) {
				if (allDivs[i].hasAttribute("id") && allDivs[i].id.match(prodPanePattern)) {
					prodPanes.push(allDivs[i]);
				}
			}
			if (prodPanes.length > 1) { // if there is only one pane, assume already visible
				// if more than one pane, make sure overview is visible and set currentProductPane to it (make sure all product panes invisible)
				var overviewPane = document.getElementById("bundleOverview");
				toggleVisibleProductPane(overviewPane); // this should cache overview in currentProductPane
	// 			toggleVisibleProductPane(prodPanes[0]);
			} else {
	// 			if (prodPanes.length < 1) {
	// 				alert("Could not find any product panes. Assuming there were some, there must be some problem.");
	// 			}
			}
		} else {
	//		alert("Could not find main container for product panes, so cannot initialize visibility. Sorry.");
		}
	} catch (m) {
		//alert("Trouble establishing initial visibility. Sorry.");
	}
}

	//// show/hide product panes (tabbed interface)

function toggleVisibleProduct(prod) {
	var thePane = document.getElementById(prod);
	if (thePane) {
		toggleVisibleProductPane(thePane);
		
		if (prod == 'gifts')
		{
		    document.getElementById('details').style.display = "none";
		} else {
		    document.getElementById('details').style.display = "block";
		}
	} else {
		alert("Could not toggle product visibility... could not find pane for: " + prod);
	}
}

function toggleVisibleProductPane(pane) {
	// find pane for 'act', hide currentProductPane, make 'act' visible & currentProductPane
	if (pane) {
		if (currentProductPane) {
			currentProductPane.style.display = "none";
		}
		pane.style.display = "block";
		currentProductPane = pane;
		
		
		
//		console.log("Reference to current product pane: %o", currentProductPane);
	} else {
		alert("Could not toggle pane visibility... invalid pane?");
	}
}


	//// countdown timer

function countdownTime() {
	/// when the page loaded, it should have established a value for remainingTime (which is number of seconds left)
	var daysLeft = parseInt(remainingTime / 86400);
	var hoursLeft = parseInt((remainingTime % 86400) / 3600);
	var minutesLeft = parseInt((remainingTime % 3600) / 60);
	var secondsLeft = remainingTime % 60;
	remainingTime--; // this explicitly assumes that we update timer once per second (& might be prone to slight drift)

	var frag = document.createDocumentFragment();
	if (daysLeft > 0) {
		var daysSpan = document.createElement("span");
		daysSpan.className = "countdown";
		daysSpan.appendChild(document.createTextNode(daysLeft));
		frag.appendChild(daysSpan);
		frag.appendChild(document.createTextNode("d "));
	}
	var hoursSpan = document.createElement("span");
	hoursSpan.className = "countdown"; // highlight each time value
	hoursSpan.appendChild(document.createTextNode(hoursLeft));
	var minutesSpan = document.createElement("span");
	minutesSpan.className = "countdown";
	minutesSpan.appendChild(document.createTextNode(minutesLeft));
	var secondsSpan = document.createElement("span");
	secondsSpan.className = "countdown";
	secondsSpan.appendChild(document.createTextNode(secondsLeft));
	frag.appendChild(hoursSpan);
	frag.appendChild(document.createTextNode("h ")); // " hr. "
	frag.appendChild(minutesSpan);
	frag.appendChild(document.createTextNode("m ")); // " min. "
	frag.appendChild(secondsSpan);
	frag.appendChild(document.createTextNode("s")); // " sec."
	return frag;
}

var cdown = null;
var cdownInterval = null;
var cprice = null; // price-changing text
var cpriceInterval = null; // interval-event to modify price
function updateCountdownString() {
// 	if (cdown == null) {
// 		var cdown = document.getElementById("cdown");
// 	}
	if (cdown) { // double-check to make sure we've got an element now
			/// instead of iterating over #cdown's childNodes to clear out children
			/// (and then replacing with new countdown information), and 
			/// especially since the countdown information is now structured,
			/// just replace the current instance of #cdown with a newly-constructed
			/// node containing the (new) timing information.
		var theParent = cdown.parentNode;
		var newTime = document.createElement("span");
		newTime.appendChild(countdownTime());
		theParent.replaceChild(newTime, cdown);
		newTime.id = "cdown";
		cdown = newTime;
	}
}

function setupCountdown() {
//	console.log("In setupCountdown()");
	try {
		cdown = document.getElementById("cdown"); // cache this so we don't have to get it from the tree every time
		if (cdown && remainingTime) { // only establish this periodic event if we found a corresponding HTML element to update initially
			cdownInterval = setInterval(updateCountdownString, 1000); // update countdown timer every second
		}
	} catch (m) {
//		console.log(m.toString());
	}
}

function incrementPrice() {
	var frag = document.createDocumentFragment();
	currPrice+=incrBump;
	var split = /(\d*\.\d\d)(\d*)/;
	var priceRound = Math.round(currPrice*Math.pow(10,6))/Math.pow(10,6);
	var priceSplit = split.exec(priceRound);
	var priceSpan = document.createElement("span");
	priceSpan.id = "visprice";
	priceSpan.className = "promoprice";
	priceSpan.appendChild(document.createTextNode("$"));
	priceSpan.appendChild(document.createTextNode(priceSplit[1]));
	var incrSpan = document.createElement("span");
	incrSpan.className = "priceincr";
	incrSpan.appendChild(document.createTextNode(zeroPad(priceSplit[2], 4)));
	frag.appendChild(priceSpan);
	frag.appendChild(incrSpan);
	return frag;
}

function zeroPad(str, len) {
	if (str.length < len) {
		for (var i=0;i<(len - str.length);i++) {
			str+="0";
		}	
	}
	return str;
}

function updatePriceString() {
	if (cprice) {
		var theParent = cprice.parentNode;
		var newPrice = document.createElement("span");
		newPrice.appendChild(incrementPrice());
		theParent.replaceChild(newPrice, cprice);
		newPrice.id = "cprice";
		cprice = newPrice;
	}
}

function setupPriceIncrement() {
	try {
		cprice = document.getElementById("cprice");
		if (cprice && incrBump && remainingTime) {
			cpriceInterval = setInterval(updatePriceString, 1000); // update every second
		}
	} catch (m) {}
}

	//// inline review posting

function addComment(promo, prod) {
		// don't actually need prod?
	requestPostReview(promo, prod);
}

function requestPostReview(promo, prod) {
	var theTextField = document.getElementById("commentField");
	if (theTextField) {
//		var theProdIdent = prodPanePattern.match(currentProductPane.id);
//		if (theProdIdent) {
			requestChange(staticLocationRelpath("revproc.php"), "promo="+promo+"&prod="+prod+"&text="+encodeURIComponent(theTextField.value), respondPostReview);
			
			
//		} else {
//			alert("Could not determine product, so cannot create comment. Please contact macupdate@macupdate.com if you continue to experience problems.");
//		}
	} else {
		alert("Could not get contents of comment field");
	}
}
function aReload() {
 // location.reload(true);
window.location.href='http://www.mupromo.com/?rord=date';
  }

function startReload() {
  setTimeout("aReload()", 2000);
  }


function respondPostReview() {
//	window.location.href=window.location.href;
	genericRespond(insertReview);
	startReload();
}

/// assume response from AJAX is node (REV) which contains the text of the review as its 
/// child (text-node), and has vis-name, ip address, and other relevent token information
/// as attributes. This way, insertReview() can be the response function for the http-request
/// and can directly access all of this information (and PHP can put together the ip, etc. 
/// when it processes the entry for inclusion). 
function insertReview() {
	// actually put review at top of reviews list
	// (if reviews are rows of table, we can do this easily by adding: theTable.insertRow()
	var theContainer = document.getElementById("reviewContainer");
	var rev = req.responseXML.getElementsByTagName("rev")[0];
	var theNewReview = null;
	//alert(rev);
	if (rev && theContainer) {
		theNewReview = createReview(rev);
		if (theContainer.className == "noreviews") { // no existing reviews, so must remove children, instead of replacing before, etc.
			var theParent = theContainer.parentNode;
			var newContainer = document.createElement("div");
//			newContainer.className = "revcontent";
//			newContainer.setAttribute("colspan", "2");
			newContainer.appendChild(theNewReview);
			theParent.replaceChild(newContainer, theContainer); // swap containers
			theContainer.id = ""; // we might not need to explicitly set this (we just want to ensure that there aren't two elements with the same id)
			newContainer.id = "reviewContainer";
			theContainer = newContainer;
		} else {
			// make this (new) review the first one (may have to re-shade things, unless CSS written accordingly)
			theContainer.insertBefore(theNewReview, theContainer.firstChild);
		}
	} else {
		alert("There was a technical problem, and a chance that your comment could not be saved. Please reload the page to see if everything was saved okay, and let us know at macupdate@macupdate.com if you continue to have problems.");
	}
	// reshade reviews
	theContainer.normalize();
	var shader = 0;
	for (var curr = theContainer.firstChild; curr != null; curr = curr.nextSibling) {
		if (curr.nodeType == 1) {
			curr.className = "comment sh"+shader;
			shader = (shader+1)%2;
		}
	}

	// flash newly created review
	var theFader = new Fader(theNewReview);
	theFader.setEndColor(233,242,255); // set to top-most shade color
	theFader.calcSteps();
	theFader.fade();
	
	// disable button for further posting (until page reload)
	var actionButton = document.getElementById("addCommentButton");
	if (actionButton) {
		actionButton.value = "Posted"; // could change src here to a "disabled" button
		actionButton.disabled = true;
	}
	var theCommentField = document.getElementById("commentField");
	if (theCommentField) {
		theCommentField.value = "";
		theCommentField.disabled = true;
	}
	
	/// giveaway: if returned XML contains winner-indication, display winning UI
	/*
	try {
		var coup = req.responseXML.getElementsByTagName("coupon");
		if (coup.length > 0) {
			// winner, so display next steps
			var theCoupon = coup[0]; // assume there's only one
//			alert("Coupon for user " + theCoupon.getAttribute("uid") + ": " + theCoupon.firstChild.nodeValue);
			var theCode = theCoupon.firstChild.nodeValue;
			placeDialog(createDialogCoupon(theCode, theCoupon.getAttribute("promo")), getOverlay());
		}
	} catch (m) {
		alert("Caught exception for coupon handling: " + m.toString());
	}
	*/
}

function createReview(revnode, sh, canmod, admin) {
	if (!sh) {
		sh = 0;
	}
	var text = revnode.firstChild.nodeValue; // assume REV contains review text as (first) text-node child (this also assumes that revnode is normalized when passed as argument here)
	var visname = revnode.getAttribute("visname");
	var theReview = document.createElement("div");
	theReview.className = "comment sh"+sh;
	theMainBody = document.createElement("table");
	theMainBody.className = "main";
	var mainRow = theMainBody.insertRow(-1); // instantiate new row
		// technically (according to the spec), mainRow is HTMLElement, so might not have insertCell() defined on it...
	var markCell = document.createElement("td");
	markCell.className = "bullet2";
	var mark = document.createElement("img");
	mark.setAttribute("src", staticLocationRelpath("newlayout/images/cloudMarker.png"));
	mark.setAttribute("width", "50");
	mark.setAttribute("height", "45");
	markCell.appendChild(mark);
	var bumpCell = document.createElement("td");
	bumpCell.className = "bump";
	var bump = document.createElement("div");
	bump.className = "bump";
	if (revnode.hasAttribute("revid")) {
		bump.id = "bump"+revnode.getAttribute("revid");
	}
	if (revnode.hasAttribute("mod")) {
		var modValue = parseInt(revnode.getAttribute("mod"));
		var bumpValue = document.createElement("div");
		bumpValue.className = "bumptot " + ((modValue < 0) ? "neg" : "pos");
			/// prepend positive values with "+"
		bumpValue.appendChild(document.createTextNode(((modValue > 0) ? "+" : "") + modValue));
		var modControls = document.createElement("div");
		modControls.className = "bumpcont";
		var upImage = document.createElement("img");
		var downImage = document.createElement("img");
		upImage.className = downImage.className = "bumpact";
		var umod = (revnode.hasAttribute("umod")) ? parseInt(revnode.getAttribute("umod")) : 0;
		if (revnode.hasAttribute("umod") && umod != 0) {
			if (umod > 0) {
				upImage.setAttribute("src", staticLocationRelpath("newlayout/images/bumpUp_off.png"));
				downImage.setAttribute("src", staticLocationRelpath("newlayout/images/bumpDown.png"));
			} else { // (umod < 0)
				upImage.setAttribute("src", staticLocationRelpath("newlayout/images/bumpUp.png"));
				downImage.setAttribute("src", staticLocationRelpath("newlayout/images/bumpDown_off.png"));
			}
		} else {
			upImage.setAttribute("src", staticLocationRelpath("newlayout/images/bumpUp.png"));
			downImage.setAttribute("src", staticLocationRelpath("newlayout/images/bumpDown.png"));
		}
		upImage.setAttribute("alt", "praise");
		downImage.setAttribute("alt", "bury");
		if (canmod && umod == 0) {
			var upControl = document.createElement("a");
			var downControl = document.createElement("a");
			upControl.setAttribute("href", "javascript:bumpUp("+revnode.getAttribute("revid")+");");
			downControl.setAttribute("href", "javascript:bumpDown("+revnode.getAttribute("revid")+");");
			upControl.appendChild(upImage);
			downControl.appendChild(downImage);
			upControl.className = "bumpUp";
			downControl.className = "bumpDown";
				// set mouseover behavior
			upImage.onmouseover = function () { this.src = staticLocationRelpath("newlayout/images/bumpUp_hot.png"); }
			upImage.onmouseout = function () { this.src = staticLocationRelpath("newlayout/images/bumpUp.png"); }
			downImage.onmouseover = function () { this.src = staticLocationRelpath("newlayout/images/bumpDown_hot.png"); }
			downImage.onmouseout = function () { this.src = staticLocationRelpath("newlayout/images/bumpDown.png"); }
				// build overall control region
			modControls.appendChild(upControl);
			modControls.appendChild(downControl);
		} else {
				// build overall control region
			modControls.appendChild(upImage);
			modControls.appendChild(downImage);
		}
		bump.appendChild(modControls);
		bump.appendChild(bumpValue);
	}
	bumpCell.appendChild(bump);

	var nameCell = document.createElement("p");
	var userName = document.createElement("span");
	userName.className = "text10";
	nameCell.className = "para6 chead";
	if (revnode.hasAttribute("uname")) {
		var nameLink = document.createElement("a");
		nameLink.setAttribute("href", "http://www.macupdate.com/users/"+revnode.getAttribute("uname"));
		nameLink.appendChild(document.createTextNode(revnode.getAttribute("uname")));
		userName.appendChild(nameLink);
	} else {
			// technically, this shouldn't happen (because everyone should be logged in and have a valid name... this isn't (historically) strictly enforced, though, so we can't stridently make this assumption)
		userName.appendChild(document.createTextNode("Anonymous"));
	}
	nameCell.appendChild(userName);
	var visdate = (revnode.hasAttribute("date")) ? reviewDate(revnode.getAttribute("date")) : "just now";
	nameCell.appendChild(document.createTextNode(" said " + visdate));

	var revtextCell = document.createElement("td");
	revtextCell.appendChild(nameCell);
	var textList = text.split(/[\r\n]+/);
	if (textList.length > 0) {
		for (var p=0; p<textList.length; p++) {
			var par = document.createElement("p");
			par.className = "para6";
			par.appendChild(document.createTextNode(textList[p]));
			revtextCell.appendChild(par);
		}
	}
		// construct mainRow
	mainRow.appendChild(markCell);
	mainRow.appendChild(revtextCell);
	mainRow.appendChild(bumpCell);
	var navRow = null;
	if (admin) {
//		var navRow = theMainBody.insertRow(-1); // for admin links, ip, etc. (we might not want this in the dynamic insert...)
		navRow = document.createElement("div");
		navRow.className = "commAdmin";
		var ipCell = document.createElement("div");
		ipCell.className = "revip";
		var blockLink = document.createElement("a");
		blockLink.setAttribute("href", staticLocationRelpath("ext/admin/block_tables.php?blval="+revnode.getAttribute("ip")));
		blockLink.appendChild(document.createTextNode("Block"));
		
		var deleteLink = document.createElement("a");
		deleteLink.setAttribute("href", staticLocationRelpath("ext/admin/deletereview.php?id="+revnode.getAttribute("revid")));
		deleteLink.appendChild(document.createTextNode("Delete"));
		
			var bump10up = document.createElement("a");
    		bump10up.setAttribute("href", "javascript:bumpUp10("+revnode.getAttribute("revid")+")"); 
    		bump10up.appendChild(document.createTextNode("bump+10"));
        
        
            	var bump10down = document.createElement("a");
        		bump10down.setAttribute("href", "javascript:bumpDown10("+revnode.getAttribute("revid")+")"); 
        		bump10down.appendChild(document.createTextNode("bump-10"));
        
		
		
		
		
		navRow.appendChild(document.createTextNode("[ "));
		navRow.appendChild(blockLink);
		navRow.appendChild(document.createTextNode(" - "));
		navRow.appendChild(deleteLink);
		navRow.appendChild(document.createTextNode(" - "));
		navRow.appendChild(bump10up);
		navRow.appendChild(document.createTextNode(" - "));
		navRow.appendChild(bump10down);
		navRow.appendChild(document.createTextNode(" ]"));
		ipCell.appendChild(document.createTextNode(revnode.getAttribute("ip")));
		navRow.appendChild(ipCell);
	}
		// mainRow and navRow should be auto-appended (via insertRow())
	theReview.appendChild(theMainBody);
	if (navRow) {
		theReview.appendChild(navRow);
	}
	return theReview;
}

function reviewDate(dt) {
	try {
		var now = new Date();
		var revd = new Date(1000*dt); // this assumes dt is seconds (date needs milliseconds)
		var datestr = 'previously';
		if (revd) {
			if (now.getTime() - revd.getTime() < 600) { // within the last 10 minutes
				datestr = 'just now';
			} else {
				var day = '';
				var time = '';
				if (now.getDate() == revd.getDate() && now.getMonth() == revd.getMonth() && now.getFullYear() == revd.getFullYear()) {
					day = 'today';
				} else {
					day = revd.getDate() + '/' + (revd.getMonth()+1);
				}
				var ampm = (revd.getHours() > 11) ? 'pm' : 'am';
				var vmin = (revd.getMinutes() < 10) ? '0'+revd.getMinutes() : revd.getMinutes();
				time = revd.getHours()+':'+vmin+ampm;
				datestr = day + " at " + time;
			}
		}
		return datestr;
	} catch (m) {
// 		console.error("Trouble building review date %o", dt);
// 		console.log("Detail: " + m.toString());
		return "previously (err)";
	}
}

var commPage = 0;
var commPageSize = 10; // if using static comment page, this should match that page size

function pageCommentsForward(promo, ord) {
	pageComments(commPage+1, promo, ord);
}

function pageCommentsBackward(promo, ord) {
	if (commPage > 0) {
		pageComments(commPage-1, promo, ord);
	}
}

function pageComments(newpage, promo, ord) {
		// do we need to do anything visually other than submit the request? (spinner, etc.)
	animateCommentMeter();
	requestCommentPage(newpage, promo, ord);
}

function requestCommentPage(pg, promo, ord) {
	requestChange(staticLocationRelpath("commpage.php"), "promo="+promo+"&page="+pg+"&size="+commPageSize+"&ord="+ord, respondCommentPage);
}

function respondCommentPage() {
	genericRespond(placeCommentPage);
}

function placeCommentPage() {
	var theContainer = document.getElementById("reviewContainer");
	var revmain = req.responseXML.getElementsByTagName("reviews")[0];
	var revlist = req.responseXML.getElementsByTagName("rev"); // this is just the (element) children of the <reviews> element gotten above...
	if (theContainer && revlist.length > 0) {
		var userCanBump = (revmain.hasAttribute("canmod") && revmain.getAttribute("canmod")=="yes");
		var userIsAdmin = (revmain.hasAttribute("admin") && revmain.getAttribute("admin")=="yes");
		var newComments = document.createDocumentFragment();
		var newContainer = document.createElement("div");
		newContainer.id="reviewContainer";
		newComments.appendChild(newContainer);
		for (var i=0; i<revlist.length; i++) {
			newContainer.appendChild(createReview(revlist[i], i%2, userCanBump, userIsAdmin));
		}
			/// swap old and new comment containers
		var containerParent = theContainer.parentNode;
		containerParent.replaceChild(newContainer, theContainer);
			/// update current page
		var main = req.responseXML.getElementsByTagName("reviews")[0];
		if (main.hasAttribute("currpage")) {
			commPage = parseInt(main.getAttribute("currpage"));
		}
	}
	stopAnimateCommentMeter();
}

var commMeter_timer = null;
var ppMeter_timer = null;

function DEPRanimateCommentMeter() {
	var steps = meterSteps();
	if (steps && steps.length > 0) {
		var s = 0;
		var curr = null;
		var prev = null;
		commMeter_timer = setInterval(
			function() {
				curr = steps[s];
				if (prev) {
					meterStepOff(prev);
				}
				meterStepOn(curr);
				s = (s+1)%steps.length;
				prev = curr;
			},
			300
		);
		
/*		
		for (var i=0; i<steps.length; i++) {
			if (i>0) {
				meterStepOff(steps[i-1]);
			}
			meterStepOn(steps[i]);
		}
*/
	}
}

function DEPRstopAnimateCommentMeter() {
	clearTimeout(commMeter_timer);
	var steps = meterSteps();
	if (steps && steps.length > 0) {
		for (var i=0; i<steps.length; i++) {
			meterStepOff(steps[i]);
		}
	}
}

function animateMeter(steps) {
	if (steps && steps.length > 0) {
		var s = 0;
		var curr = null;
		var prev = null;
		return setInterval(
			function() {
				curr = steps[s];
				if (prev) {
					meterStepOff(prev);
				}
				meterStepOn(curr);
				s = (s+1)%steps.length;
				prev = curr;
			},
			300
		);
	}
}

function stopAnimateMeter(steps, timer) {
	clearTimeout(timer);
	if (steps && steps.length > 0) {
		for (var i=0; i<steps.length; i++) {
			meterStepOff(steps[i]);
		}
	}
}

function animateCommentMeter() {
	commMeter_timer = animateMeter(meterSteps());
}

function stopAnimateCommentMeter() {
	stopAnimateMeter(meterSteps(), commMeter_timer);
}

function animatePastPromosMeter() {
	ppMeter_timer = animateMeter(ppMeterSteps());
}

function stopAnimatePastPromosMeter() {
	stopAnimateMeter(ppMeterSteps(), ppMeter_timer);
}

function findSteps(elem) {
	var steps = new Array();
	var theContainer = document.getElementById(elem);
	if (theContainer && theContainer.hasChildNodes()) {
		for (var curr = theContainer.firstChild; curr != null; curr = curr.nextSibling) {
			if (curr.nodeType == Node.ELEMENT_NODE && curr.className == "pageComm_meter") {
				steps.push(curr);
			}
		}
	} else {
//		console.error("Could not find container, or could not find meter children");
	}
	return steps;
}	

function meterSteps() { return findSteps("commentNavigation"); }
function ppMeterSteps() { return findSteps("ppNavSteps"); }

	/// old comment meter step finder
function DEPRmeterSteps() {
	var steps = new Array();
	var theContainer = document.getElementById("commentNavigation");
	if (theContainer && theContainer.hasChildNodes()) {
		for (var curr = theContainer.firstChild; curr != null; curr = curr.nextSibling) {
			if (curr.nodeType == Node.ELEMENT_NODE && curr.className == "pageComm_meter") {
				steps.push(curr);
			}
		}
	} else {
//		console.error("Could not find container, or could not find meter children");
	}
	return steps;
}

function meterStepOn(img) {
	if (img && img.src) {
		img.src = "http://www.macupdate.com/promo/newlayout/images/commMarkerFull.png";
	} else {
//		console.log("No marker");
	}
}

function meterStepOff(img) {
	if (img && img.src) {
		img.src = "http://www.macupdate.com/promo/newlayout/images/commMarkerEmpty.png";
	} else {
//		console.log("No marker");
	}
}
	
	//// paging past-promo-purchase box
var ppPage = 0;
var ppPageSize = 10; // static content should match this size (cf generation)
var ppColSize = 2; // number of columns to display

function pagePastPromosForward() {
	pagePastPromos(ppPage+1);
}

function pagePastPromosBackward() {
	if (ppPage > 0) {
		pagePastPromos(ppPage-1);
	}
}

function pagePastPromos(newpage) {
	/// any animation? (like animateCommentMeter()?)
	animatePastPromosMeter();
	requestPastPromoPage(newpage);
}

function requestPastPromoPage(pg) {
	requestChange("/pastPromoPage.php", "page="+pg, respondPastPromoPage);
}

function respondPastPromoPage() {
	genericRespond(placePastPromoPage);
}

function placePastPromoPage() {
	var theContainer = document.getElementById("pastPurchase");
	var pastlist = req.responseXML.getElementsByTagName("past");
	if (theContainer && pastlist.length > 0) {
			/// build new list	
		var newPastPages = document.createDocumentFragment();
		var newContainer = document.createElement("div");
		newPastPages.appendChild(newContainer);
			/// need to put these in tables, now, as many as ppColSize
		var listContainer = document.createElement("table");
		listContainer.id = "ppLists";
		listContainer.setAttribute("cellpadding", "0");
		listContainer.setAttribute("cellspacing", "0");
		var listContainRow = listContainer.insertRow(-1);
		var perColumn = Math.ceil(ppPageSize / ppColSize);
		var item = 0;
		for (var col=0; col < ppColSize; col++) {
			if (col > 0) { // insert gutter column
				var theGutter = listContainRow.insertCell(-1);
				theGutter.className = "ppListGutter";
			}
			var theColumn = listContainRow.insertCell(-1);
			theColumn.className = "ppListColumn";
			var theList = document.createElement("table");
			theList.setAttribute("cellpadding", "0");
			theList.setAttribute("cellspacing", "0");
			for (var i=0; i<perColumn && item<pastlist.length; i++) {
				theList.appendChild(ppItem(pastlist[item]));
				item++;
			}
			theColumn.appendChild(theList);
		}
		newContainer.appendChild(listContainer);
		newContainer.id = "pastPurchase";
			/// swap old and new containers
		var containerParent = theContainer.parentNode;
		containerParent.replaceChild(newContainer, theContainer);
			/// update counter
		var mainNode = req.responseXML.getElementsByTagName("promo")[0];
		var prevPage = ppPage;
		ppPage = parseInt(mainNode.getAttribute("page"));
		if (ppPage == 0) {
			disablePastPromosBackward();
			if (pastlist.length < ppPageSize || (mainNode.hasAttribute("last") && ppPage == parseInt(mainNode.getAttribute("last")))) {
				disablePastPromosForward();
			} else {
				enablePastPromosForward();
			}
		} else if (mainNode.hasAttribute("last") && ppPage == parseInt(mainNode.getAttribute("last"))) { 
			disablePastPromosForward();
			enablePastPromosBackward();
		} else { // otherwise, how best to ensure that both links are enabled without re-enabling?
			if (ppPage > 0) { // room to go back
				enablePastPromosBackward();
			} else if (ppPage < prevPage) { // moving backward, so must be able to go forward
				enablePastPromosForward();
			}
		}
	}
	stopAnimatePastPromosMeter();
}

function requestInitialPastPromoPage() {
		/// to show first popup, use this callback: function ()  { respondPastPromoPage(); showFirstPopup(); }
	requestChange("/pp-test.xml", null, respondPastPromoPage);
}

function showFirstPopup() {
	var theList = findAllPopups(document.getElementById("pastPurchase"));
	if (theList && theList.length) {
		setPopupVisibility(theList[0], "show");
	}
}

function ppItemComprehensive(elt) {
	elt.normalize();
//	var theListItem = document.createElement("li");
	var theListRow = document.createElement("tr");
	var theListItem = theListRow.insertCell(-1);
	theListItem.className = "ppItem";
	var theLink = document.createElement("a");
	theLink.setAttribute("href", "/deal/"+elt.getAttribute("pid")+"/"+elt.getAttribute("vis"));
	theLink.appendChild(document.createTextNode(elt.firstChild.nodeValue + " $" + elt.getAttribute("price") + " (" + elt.getAttribute("disc") + "% off)")); // promo title
	theListItem.appendChild(theLink);
		/// instead of pre-creating popups, we could create them onmouseover with a closure:
		/// theListItem.onmouseover = function() { theListItem.appendChild(ppItemPopup(elt)); }
		/// (but how could we handle this for the initial items?)
	theListItem.appendChild(ppItemPopup(elt)); // include (hidden) popup
	theLink.onmouseover = function() { showPopup(theListItem); };
	theLink.onmouseout = function() { hidePopup(theListItem); };
//	return theListItem;
	return theListRow;
}

function ppItem(elt) {
	elt.normalize();
//	var theListItem = document.createElement("li");
	var theListRow = document.createElement("tr");
	var theTitleCell = theListRow.insertCell(-1);
	theTitleCell.className = "titleTOP";
	var theDiscountCell = theListRow.insertCell(-1);
	theDiscountCell.className = "discount";
	theListRow.className = "ppItem";
	var theLink = document.createElement("a");
	theLink.setAttribute("href", "/deal/"+ppItem_id(elt)+"/"+ppItem_linkTitle(elt));
	theLink.appendChild(document.createTextNode(ppItem_title(elt))); // promo title
	var theTitleContent = document.createElement("div"); // embedding trick for positioning popup
//	theTitleCell.appendChild(theLink);
	theTitleContent.appendChild(theLink);
	theTitleContent.className = "titleContent";
	theTitleCell.appendChild(theTitleContent);
	theDiscountCell.appendChild(document.createTextNode(ppItem_discount(elt) + "% off"));
		/// instead of pre-creating popups, we could create them onmouseover with a closure:
		/// theListItem.onmouseover = function() { theListItem.appendChild(ppItemPopup(elt)); }
		/// (but how could we handle this for the initial items?)
	theTitleContent.appendChild(ppItemPopup(elt)); // include (hidden) popup
	theLink.onmouseover = function() { showPopup(theTitleCell); };
	theLink.onmouseout = function() {
		hidePopup(theTitleCell, theLink); 
	};
//	return theListItem;
	return theListRow;
}

function ppItemPopup(elt) {
	var thePopup = document.createElement("div");
	thePopup.className = "ppPopup";
	var thePopupContent = document.createElement("div");
	thePopupContent.className = "popMain";
	var theProductTitle = document.createElement("div");
	theProductTitle.className = "popTitle";
	theProductTitle.appendChild(document.createTextNode(ppItem_title(elt)));
	var theProductDescr = document.createElement("div");
	theProductDescr.className = "popDescr";
	theProductDescr.appendChild(document.createTextNode(truncateDescr(ppItem_descr(elt))));
	thePopupContent.appendChild(ppItem_icon(elt));
	thePopupContent.appendChild(theProductTitle);
	thePopupContent.appendChild(theProductDescr);
	thePopupContent.appendChild(popupPriceCapsule(ppItem_price(elt)));
	thePopup.appendChild(thePopupContent);
	return thePopup;
}

function popupPriceCapsule(price) {
// 	var theCapsule = document.createElement("div");
// 	theCapsule.className = "popPrice";
	var theCapsuleLayout = document.createElement("table");
//	theCapsuleLayout.className = "popPriceLayout";
	theCapsuleLayout.className = "popPrice";
	theCapsuleLayout.setAttribute("cellpadding", "0");
	theCapsuleLayout.setAttribute("cellspacing", "0");
	var theCapsuleRow = theCapsuleLayout.insertRow(-1);
	var theCapsuleLeft = theCapsuleRow.insertCell(-1);
	var theCapsulePrice = theCapsuleRow.insertCell(-1);
	var theCapsuleRight = theCapsuleRow.insertCell(-1);
	theCapsulePrice.className = "priceValue";
	theCapsulePrice.appendChild(document.createTextNode("$"+price));
	var theLeftEnd = document.createElement("img");
	var theRightEnd = document.createElement("img");
	theLeftEnd.setAttribute("src", "http://www.macupdate.com/promo/newlayout/images/popup_price_left.png");
	theRightEnd.setAttribute("src", "http://www.macupdate.com/promo/newlayout/images/popup_price_right.png");
	theCapsuleLeft.className = "priceEnd";
	theCapsuleRight.className  = "priceEnd";
	theCapsuleLeft.appendChild(theLeftEnd);
	theCapsuleRight.appendChild(theRightEnd);
//	theCapsule.appendChild(theCapsuleLayout);
	return theCapsuleLayout;
}

function showPopup(item) {
	hideAllPopups();
	setPopupVisibility(findItemPopup(item), "show");
}

function hidePopup(item) {
	setPopupVisibility(findItemPopup(item), "hide");
}

function hideAllPopups() {
	var thePopups = findAllPopups(document.getElementById("pastPurchase"));
	if (thePopups && thePopups.length > 0) {
		for (var p=0; p<thePopups.length; p++) {
			setPopupVisibility(thePopups[p], "hide"); // may want this to be abstracted better
		}
	}
}

	/// this depends deeply on the actual structure of the result page as above
function findAllPopups(contain) {
	var rows = contain.getElementsByTagName("tr");
	var pops = new Array();
	for (var r=0; r<rows.length; r++) {
		if (rows[r].className == "ppItem") {
			var divs = rows[r].getElementsByTagName("div");
			if (divs && divs.length > 0) {
				var found = false;
				for (var d = 0; d < divs.length && !found; d++) {
					if (divs[d].className == "ppPopup") {
						pops.push(divs[d]);
					}
				}
			}
		}
	}
	return pops;
}

function delay_hidePopup(item, triggerRegion) {
	var thePopup = findItemPopup(item); // now we can establish a new mouseover event to cancel the timer
	var theHideTimer = setTimeout(function() {
		setPopupVisibility(thePopup, "hide");
	}, 1000);
		/// (we could annotate over/out actions with console.log so we can follow when they're triggered)
		/// don't trigger popup-hiding when cursor over popup...
	thePopup.onmouseover = function() { 
//		console.log("Trigger onmouseover %o", thePopup);
		clearTimeout(theHideTimer); 
	}
		/// but, now, do close it when we leave
	thePopup.onmouseout = function() { 
//		console.log("Trigger onmouseout %o", thePopup);
		setPopupVisibility(thePopup, "hide"); 
	}
}

	/// item promo id from XML
function ppItem_id(elt) {
	return elt.getAttribute("pid");
}

function ppItem_prodid(elt) {
	var prod = ppItem_prodNode(elt);
	if (prod) {
		return prod.getAttribute("prodid");
	} else {
		return null;
	}
}
	/// visible title from XML node
function ppItem_title(elt) {
	var prod = ppItem_prodNode(elt);
	return prod.firstChild.nodeValue;
}

function ppItem_vers(elt) {
	if (elt.hasChildNodes) {
		var prod = ppItem_prodNode(elt);
		return prod.getAttribute("vers");
	}
	return "";
}
	/// product description (right now this assumes no multi-way promos)
function ppItem_descr(elt) {
	return (elt.hasAttribute("descr")) ? elt.getAttribute("descr") : "";
}
	/// discount from XML node
function ppItem_discount(elt) {
	return elt.getAttribute("disc");
}

function ppItem_price(elt) { 
	return elt.getAttribute("price");
}
	/// title suitable for including in link construction
function ppItem_linkTitle(elt) {
	var prod = ppItem_prodNode(elt);
	return ((prod) ? prod.getAttribute("vis") : null);
//	return elt.firstChild.getAttribute("vis");
}

function ppItem_hasIcon(elt) {
	var prod = ppItem_prodNode(elt);
	return (prod.hasAttribute("icon") && prod.getAttribute("icon") == "yes");
}

function ppItem_icon(elt) {
	var theImage = document.createElement("img");
	if (ppItem_hasIcon(elt)) {
		theImage.setAttribute("src", "http://www.mupromo.com/images/icons/"+ppItem_prodid(elt)+".png");
	} else {
		theImage.setAttribute("src", "http://www.mupromo.com/images/icons/app.png");
	}
	theImage.className = "ppIcon";
	return theImage;
}

function ppItem_prodNode(elt) {
	return elt.getElementsByTagName("prod")[0];
}	

function findItemPopup(item) {
	var pop = null;
	if (item.nodeType && item.hasChildNodes) {
		var cands = item.getElementsByTagName("div");
		if (cands.length > 0) {
			for (var i=0; i<cands.length; i++) {
				if (cands[i].className == "ppPopup") {
					return cands[i];
				}
			}
		}
	}
	return pop;
}

function titleCellName(cell) {
	if (cell.hasChildNodes && cell.firstChild && cell.firstChild.hasChildNodes && cell.firstChild.firstChild) {
		return cell.firstChild.firstChild.firstChild.nodeValue;
	}
}

	//// truncate long descriptions
function truncateDescr(descr) {
	return descr;
}

function setPopupVisibility(pop, vis) {
	if (pop) {
		if (vis == "show") {
			pop.style.display = "block";
		} else if (vis == "hide") {
			pop.style.display = "none";
		}
	}
}

function enablePastPromosControl(ctlid, action) {
	try {
		var theControl = document.getElementById(ctlid);
		if (!theControl.hasAttribute("href")) {
			theControl.setAttribute("href", action);
		}
	} catch (m) {
//		console.log("Problem enabling control (%o): %o", ctlid, m);
	}
}

function enablePastPromosForward() {
	enablePastPromosControl("ctl_pastPromosForward", "javascript:pagePastPromosForward()");
}

function enablePastPromosBackward() {
	enablePastPromosControl("ctl_pastPromosBackward", "javascript:pagePastPromosBackward()");
}

function disablePastPromosForward() {
	try {
		disablePPControl(document.getElementById("ctl_pastPromosForward"));
	} catch (m) {
//		console.log("Problem disabling forward control: " + m.toString());
	}
}

function disablePastPromosBackward() {
	try {
		disablePPControl(document.getElementById("ctl_pastPromosBackward"));
	} catch (m) {
//		console.log("Problem disabling backward control: " + m.toString());
	}
}

function disablePPControl(ctl) {
	if (ctl && ctl.hasAttribute("href")) {
		ctl.removeAttribute("href");
	}
}

	//// handle coupon dialog

function createDialogCoupon(code, promo) {
	var theDialog = createDlgComp_contain("lightbox"); // fix id for dialog container here (no reason to make parameter)
	/// create and display login/email form
	var theContents = document.createDocumentFragment();

	var couponLine = document.createElement("div");
	var coupPar1 = document.createElement("p");
	coupPar1.className = "couponPar";
	coupPar1.appendChild(document.createTextNode("Congratulations! You won a free registered copy of today's MacUpdate Promo."));
	var coupPar2 = document.createElement("p");
	coupPar2.className = "couponPar";
	coupPar2.appendChild(document.createTextNode("Two promos are given away for free every day to random people who post comments about today's MUPromo. Your comment has been posted. Now go redeem your free registration!"));
	couponLine.appendChild(coupPar1);
	couponLine.appendChild(coupPar2);

	theContents.appendChild(couponLine);

	var theGoButton = createDlgComp_button("Redeem Free Copy", function() {cancelDialog(theDialog); window.location.href = "purchreg.php?pid="+promo; });
	theGoButton.id = "goButton";
	theContents.appendChild(theGoButton);
	theContents.appendChild(document.createTextNode(" "));
	var theCancelButton = createDlgComp_button("Close", function() {cancelDialog(theDialog);});
	theCancelButton.className = "cancelButton";
	theCancelButton.id = "couponButton";
	theContents.appendChild(theCancelButton);

	theDialog.appendChild(createDlgComp_mat(theContents, null)); // really, we probably want spiffy lightbox effect here
	return theDialog;
}

function createDialogInviteCoupon(code, promo) {
	var theDialog = createDlgComp_contain("lightbox"); // fix id for dialog container here (no reason to make parameter)
	/// create and display login/email form
	var theContents = document.createDocumentFragment();

	var couponLine = document.createElement("div");
	var coupPar1 = document.createElement("p");
	coupPar1.className = "couponPar";
	coupPar1.appendChild(document.createTextNode("Congratulations! You won a free registered copy of this MacUpdate Promo."));
	var coupPar2 = document.createElement("p");
	coupPar2.className = "couponPar";
	coupPar2.appendChild(document.createTextNode("You have two options to redeem your prize: if you'd prefer a refund on the cost of your previous purchase, select 'Refund'; or, to obtain another copy of the promo at no additional charge, choose 'Free Copy'."));
	couponLine.appendChild(coupPar1);
	couponLine.appendChild(coupPar2);

	theContents.appendChild(couponLine);
	
	var theRefundButton = createDlgComp_button("Refund", function() {cancelDialog(theDialog); submitRefundRequest(code, promo);});
	theRefundButton.id = "refundButton";
	theContents.appendChild(theRefundButton);
	theContents.appendChild(document.createTextNode(" "));
	var theGoButton = createDlgComp_button("Free Copy", function() {cancelDialog(theDialog); window.location.href = "purchreg.php?pid="+promo; });
	theGoButton.id = "goButton";
	theContents.appendChild(theGoButton);
	theContents.appendChild(document.createTextNode(" "));
	var theCancelButton = createDlgComp_button("Close", function() {cancelDialog(theDialog);});
	theCancelButton.className = "cancelButton";
	theCancelButton.id = "couponButton";
	theContents.appendChild(theCancelButton);

	theDialog.appendChild(createDlgComp_mat(theContents, null)); // really, we probably want spiffy lightbox effect here
	return theDialog;
}

function submitRefundRequest(code, promo) {
	requestChange("refund.php", "code="+code+"&promo="+promo, respondRefund);
}

function respondRefund() {
	genericRespond(reportRefund);
}

function reportRefund() {
	alert("Your refund request has been submitted, and will be processed shortly. Congratulations, and thanks for your support!");
}

function cancelDialog(dlgNode) {
//	cancelRequest();
	removeDialog(dlgNode);
	removeOverlay(getOverlay());
}

function getOverlay() {
	var over = document.getElementById("overlay");
	if (!over) {
		over = document.createElement("div");
		over.id = "overlay";
		document.body.appendChild(over);
	}
	return over;
}

/********* bundle specifics ***********/
var bundleDescrDefault = null;

function bundleDescr() {
	return document.getElementById("bundleProductDescr");
}

function hiliteProductDescr(title, descr, sep) {
	try {
		if (sep == null) {
			sep = " - ";
		}

		var theStatLine = bundleDescr();
			// if there is already content, clear it out
		removeProductDescr(true);
		theStatLine.appendChild(createProductDescr(title, descr, sep));
	} catch (m) {
		alert("Caught exception changing bundle description line: " + m.toString());
	}
}

function markTab(tab) {
	if (tab) {
		try {
				// clear existing tab hlite
			var theRow = tab.parentNode;
			if (theRow.cells.length > 0) { // otherwise something is seriously wrong!
				for (var c=0; c<theRow.cells.length; c++) {
//					theRow.cells[c].removeAttribute("class"); 
					if (theRow.cells[c].id != "pTabLeft" && theRow.cells[c].id != "pTabRight") {
						theRow.cells[c].className = "icon"; // removes existing hlTab class
					}
				}
			} else {
				alert("Serious 'splainin to do: can't find siblings of row-to-mark");
			}
				// hilite this tab
			tab.className = "hlTab icon";
		} catch (m) {
			alert("Problem marking tab: " + m.toString());
		}
	}
}

function tabForProduct(pid) {
	return document.getElementById("ptab"+pid);
}

function createProductDescr(title, descr, sep) {
	if (sep == null) {
		sep = " - ";
	}
	var theDescr = document.createDocumentFragment();
	var theTitle = document.createElement("span");
	theTitle.className = "bundescrTitle";
	theTitle.appendChild(document.createTextNode(title));
	var theProdDescr = document.createElement("span");
	theProdDescr.className = "bundescrDescr";
	theProdDescr.appendChild(document.createTextNode(descr));
	var theSep = document.createTextNode(sep); // what to put between title and descr
	theDescr.appendChild(theTitle);
	theDescr.appendChild(theSep);
	theDescr.appendChild(theProdDescr);
	return theDescr;
}

function setDescrDefault(nd) {
//	alert("setting descr default: " + nd);
	bundleDescrDefault = nd;
}

function setProdDescrDefault(title, descr, sep) {
//	alert("setting descr default for " + title);
	setDescrDefault(createProductDescr(title, descr, sep));
}

function setOverviewDescrDefault(txt) {
	var theDescr = document.createDocumentFragment();
	theDescr.appendChild(document.createTextNode(txt));
	setDescrDefault(theDescr);
}

function removeProductDescr(clr) {
	if (clr == null) { // control whether to clear description area, or replace with default (if available)
		clr = false;
	}
	try {
		var theStatLine = bundleDescr();
		while (theStatLine.hasChildNodes()) {
			theStatLine.removeChild(theStatLine.firstChild);
		}
		if (!clr && bundleDescrDefault && bundleDescrDefault.hasChildNodes()) {
			theStatLine.appendChild(bundleDescrDefault.cloneNode(true)); // need to place a copy of the doc frag, not the fragment itself (otherwise, clearing out the region destroys the actual nodes and refs)
		}
	} catch (m) {
		alert("caught exception on removeProductDescr: " + m.toString());
	}
}
