/**
 * @fileOverview
 * MI modifications to the gSiteLife object.
 *
 * These mods apply for simple widget functionality and this file should be
 * included immediately after the scripts included from Pluck.
 *
 * Everything should be namespaced into the gSiteLife.mi object.
 *
 * @author Joe Whetzel
 * @namespace gSiteLife.mi
 */

gSiteLife.mi = {};

// variable setting the default location to return to after submitting a
// comment, the argument identifies to Omniture that a Pluck comment has been
// submitted.
//The qwxq variable is filled with a random value to force page reload upon multiple submissions
var urlTS = new Date();
gSiteLife.mi.commenting = {
	submitReturnAddress: 'http://' + location.host + location.pathname + "?mi_pluck_action=comment_submitted&qwxq="+Math.floor(Math.random()*10000)+urlTS.getMilliseconds()+"#Comments_Container"
};

/**
 * Returns boolean true or false indicating whether or not the current user is 
 * logged in. Status is determined via agreement by both Pluck and Insite, 
 * meaning if either doesn't recognize you as logged in you're not.
 * To determine this function checks the availability of the Pluck HD or AT
 * cookie, and the Insite *_user_auth cookie.
 * For efficiency, if the variable isn't set the results will be stored in 
 * gSiteLife.mi.loggedIn for future reference.
 */
gSiteLife.mi.userLoggedIn = function() {
	if (this.loggedIn === undefined) {
		var cookies=document.cookie.split(';');
		var insiteCrumb = false;
		var pluckCrumb = false;
		for (var i=cookies.length-1; i>=0; i--) {
			if (cookies[i].match(/^\s*AT=/) || cookies[i].match(/^\s*HD=/)) { pluckCrumb=true; }
			else if (cookies[i].match(/_user_auth=/)) { insiteCrumb=true; }
		}
		this.loggedIn = (insiteCrumb && pluckCrumb);
	}
	return this.loggedIn;
}


/**
 * @fileOverview
 * Nyx is not just the goddess of the night, she's also a mini-library of DAAPI reference gadgets.
 * 
 * This file includes scripts used throughout Nyx; defines:
 * <li>(if needed) console -- a safety wrapper for Firebug-type logging calls</li>
 * 
 * Everything (except console) is namespaced into the global object NYX.
 * 
 * @author Glen Ford (glenford [at] yahoo.com)
 * @namespace NYX
 */

//create the wrapper object for the namespace (with version number and other key defaults)
var NYX = {
	version: "0.6"
};

/* goof-proof calls to the console -- IMMEDIATE EXECUTION HERE
 * Defines a console object (with "empty" methods) as needed; allows code in any browser to
 * call Firebug console methods without error. Doesn't overwrite existing console so as to not
 * interfere with Safari's console. The Firebug methods array should be maintained in sync with
 * the actual Firebug console.
 * 
 * TODO: this could be enhanced to make dir do something meaningful in the Safari console
 */
NYX.fixConsole = function() {
	if (typeof window.console != "object") {window.console = {};}
	if (window.console.isNyxxed) {/*already fixed*/}
	else {
		var firebugMethods = ["log","debug","info","warn","error","assert","dir","dirxml",
			"trace","group","groupEnd","time","timeEnd","profile","profileEnd","count"];
		for (var methodIdx = 0; methodIdx < firebugMethods.length; methodIdx++) {
			var methodName = firebugMethods[methodIdx];
			if (typeof window.console[methodName] != "function") {window.console[methodName] = function(){};}
		}
	}
	//add our tracking flag
	window.console.isNyxxed = true;
};
NYX.fixConsole();


//now let's report the version (totally optional)
console.info("Nyx library loaded, version " + NYX.version);



//the cache is a hash for object instances to store "globals" without affecting the window namespace
NYX.cache = {};




/* this apparently fixes the horrid IE Operation Aborted error pretty simply;
 * based on a trick by Diego Perini -- http://javascript.nwbox.com/IEContentLoaded/
 * (it's an object instance in order to handle objects parameters sent to the action function)
 */
NYX.ieSafeExecution = function() {
	//store this in a closure, in case we do asynchronous operation
	var This = this;
	
	//a setting
	this.timeoutLength = 200;	//millis
	
	//the first argument must be the function to call; any remaining arguments get passed to that function
	if (typeof arguments[0] != "function") {
		throw("First parameter to NYX.ieSafeExecution is required and must be a function");
	} else {
		this.functionToCall = arguments[0];
	}
	
	this.execute = function() {
		//on the first call, we need to cache the arguments
		if (typeof This.arguments == "undefined") {This.arguments = arguments;}
		
		if ( typeof document.body.attachEvent == "object" && (document.readyState != "loaded" && document.readyState != "complete") ) {
			//document.title += "[IE]"; //for visual debugging
			if (typeof console == "object") {console.log("in the IE block");}
			try {
				//document.title += "^trying^"; //for visual debugging
				document.documentElement.doScroll("left");
				This.functionToCall.apply(This.functionToCall, This.arguments);
			} catch(error) {
				setTimeout(This.execute, This.timeoutLength);
			}
		} else {
			if (typeof console == "object") {console.log("executing immediately");}
			This.functionToCall.apply(This.functionToCall, This.arguments);
		}
	};
};


//NYX utilities
NYX.util = {};

NYX.util.makePuid = function(optExtraDigits, optBase) {
	/* returns a string based on the current time and a pseudorandom number that is 
	 * _probably_ unique... not quite a GUID, but pretty secure
	 * pass a larger number for optExtraDigits for more security
	 * optBase controls how the number gets converted -- a higher number results in a smaller string
	 */
	var timeSeed, rnd, puid;
	if (typeof optExtraDigits != "number") {optExtraDigits = 5;}
	if (typeof optBase != "number") {optBase = 32;}
	timeSeed = ( new Date().valueOf() ) - Date.parse("1/1/2008");
	rnd = Math.random().toString().substr(2, optExtraDigits); //expects Math.random to return x.YYY...
	puid = parseInt(timeSeed + "" + rnd).toString(optBase);
	
	return puid;
};

NYX.util.querystring = {
	/**
	 * @return {String} the value of the specified querystring parameter;
	 * 					null if the variable isn't defined
	 * @param {String} name				Name of the QS variable
	 */
	get: function(name) {
		var key = name + "=";
		var nameValuePairs = document.location.search.substring(1).split("&");
		for (var pairIdx = 0; pairIdx < nameValuePairs.length; pairIdx++) {
			if ( nameValuePairs[pairIdx].indexOf(key) === 0 ) {
				return nameValuePairs[pairIdx].substring(key.length);
			}
		}
		return null;
	}
	,
	/**
	 * Sets or adds the specified querystring parameter. Returns the querystring with a 
	 * leading "?" even if it didn't have one before.
	 * 
	 * <p><i>Notes:</i>
	 * 	If the parameter exists, it <b>must</b> have the trailing equals sign, even if the value is empty.
	 * 	Names are case sensitive.
	 * </p>
	 * 
	 * @return {String} <code>location.search</code> or the optional querystring argument
	 * 					with the given parameter set to the value passed.
	 * @param {String} name				Name of the QS variable
	 * @param {String} value			Value to set
	 * @param {String} [optExistingQS]	QS to use (defaults to location.search)
	 */
	set: function(name, value, optExistingQS) {
		//get the qs to work with 
		var qs;
		if (typeof optExistingQS != "string") {
			qs = location.search;
		} else {
			qs = optExistingQS;
		}
		var theReturn = qs;
		
		if (typeof value == "undefined") {value = "";}
		
		//escape the name-value pair
		var nvp = encodeURI(name + "=" + value);
		
		if (qs === "") {
			theReturn = nvp;
		} else if ( !NYX.util.string.contains(qs, name) ) {
			theReturn = qs + "&" + nvp;
		} else {
			//use a regex to replace the variable
			var regex = new RegExp("(" + name + "=[^&^]*)", "gi");
			
			//this test also loads the global RegExp object
			if ( qs.match(regex) !== null ) {
				theReturn = qs.replace(RegExp.lastMatch, nvp);
			}
		}
		
		if ( !NYX.util.string.startsWith(theReturn, "?") ) {theReturn = "?" + theReturn;}
		return theReturn;
	}
};

NYX.util.obj = {
	/** 
	 * Mimics the Java <code>extends</code> keyword.
	 * Copies all members (properties and methods) from the parent into the target.
	 * <p>In addition to inheritance, it's useful for copying "parameter bags" (JSON objects) into an instance.</p>
	 * <p>To override members, change them after the <code>extendObj</code> call.</p>
	 * @param {Object} targetClass	The child class to modify.
	 * @param {Object} parentClass	The class to extend from.
	 */
	extend: function(targetClass, parentClass) {
		for (var member in parentClass) {
			targetClass[member] = parentClass[member];
		}
		return targetClass;
	}
};

NYX.util.array = {
	contains: function(theArray, match) {
		if (theArray.length) {
			for (var idx = 0; idx < theArray.length; idx++) {
				if (theArray[idx] == match) {
					return true;
				}
			}
		}
		return false;	//if we get here, there's no match
	}
};

NYX.util.string = {
	/**
	 * Checks source for presence of match. Source is converted to a string as needed,
	 * but that doesn't guarantee this will work for arrays. The caller must manipulate arrays as needed
	 * if using this function to check for an element.
	 * 
	 * @return {Boolean} true if the string contains match
	 * 
	 * @param {String} source	string to search through
	 * @param {String} match	string to search for
	 * @param {Boolean} [optIgnoreCase]	if true, case is ignored
	 */
	contains: function(source, match, optIgnoreCase) {
		if (optIgnoreCase) {
			source = source.toLowerCase();
			match  = match.toLowerCase();
		}
		return (source.indexOf(match) > -1);
	}
	,
	/**
	 * @return {String} this string with all occurrences of the substring replaced
	 * @param {String} source	string to search through
	 * @param {String} match		The sub-string to kill
	 * @param {String} replacement	
	 */
	replaceAll: function(source, match, replacement) {
		// in js regular expressions the $ character paired with some others represents
		// a special replacement pattern, so we replace $ with a custom pattern
		// after everything is done replacing we then go back and replace our custom
		// pattern with the desired $
		replacement = replacement.replace(/\$/g, 'tom MI.dollar sawyer');
		while ( this.contains(source, match) ) {
			source = source.replace(match, replacement);
		}
		source = source.replace(/tom MI\.dollar sawyer/g,'$');
		return source;
	}
	,
	/**
	 * @return {String} A 0-length string for nulls/undefined argument; else the argument as a string.
	 */
	ensure: function(arg) {
		//converts arg to a string even if it is null, etc.
		if (typeof arg == "string") {return arg;}
		if (arg === null || typeof arg == "undefined") {return "";}
		return arg.toString();
	}
	,
	/** Replaces leading and trailing whitespace. */
	trim: function(stringToTrim) {
		stringToTrim = this.ensure(stringToTrim);
		return stringToTrim.replace(/(^\s+|\s+$)/g, "");
	}
	,
	/** @return {Boolean} true if the string passed is 0-length or only whitespace */
	isBlank: function(source) {
		return (this.trim(source) === "");
	}
	,
	/** 
	 * @return {Boolean} true if the string starts with the fragment passed (case-sensitive)
	 * @param {String} source	string to search through
	 * @param {String} match	Fragment to look for
	 * @param {Boolean} [optIgnoreCase]	if true, case is ignored
	 */
	startsWith: function(source, match, optIgnoreCase) {
		return ( source.substring(0, match.length) == match );
	}
	,
	/** 
	 * @return {Boolean} true if the string ends with the fragment passed (case-sensitive)
	 * @param {String} source	string to search through
	 * @param {String} match	Fragment to look for
	 * @param {Boolean} [optIgnoreCase]	if true, case is ignored
	 */
	endsWith: function(source, match, optIgnoreCase) {
		if (optIgnoreCase) {
			source = source.toLowerCase();
			match  = match.toLowerCase();
		}
		return ( source.substring(source.length - match.length) == match );
	}
};

NYX.util.dom = {
	/**
	 * Returns an array of <b>direct</b> child elements that have the given 
	 * CSS class. (This is based on Prototype, but their way causes weird bugs in IE.)
	 * 
	 * @return {DOM:Element[]} Returns an empty array if nothing found.
	 * @param {String} 		className to match
	 * @param {DOM:Element}	[optElem]	if not passed, <code>document.body</code> is used
	 */
	getElementsByClass: function(className, optElem) {
		var theReturn = [];
		if (typeof optElem == "undefined") {var elem = document.body;}
		
		var children = elem.getElementsByTagName("*");
		for (var childIdx = 0; childIdx < children.length; childIdx++) {
			var child = children[childIdx];
			if ( typeof child.className == "string" && this.elemHasClass(child, className) ) {
				theReturn.push(child);
			}
		}
		return theReturn;
	},
	/**
	 * @return {Boolean} True if the DOM element has the CSS class applied to it.
	 * @param {DOM:Element}	elem		
	 * @param {String} 		className	
	 */
	elemHasClass: function(elem, className) {
		var currentClasses = elem.className.toLowerCase().split(/\s+/g);
		return NYX.util.array.contains( currentClasses, className.toLowerCase() );
	}
};

//constructors
NYX.TemplateTool = { //"ABSTRACT" superclass!
	//template part names -- internal use, but can be overridden
	DOM_TARGET_SUFFIX:  "dynamicContent",
	DOM_WAITMSG_SUFFIX: "waitMsg",
	
	//classes for item coloring (see below)
	ALT_CLASS_2: "nyx2",
	ALT_CLASS_3: "nyx3",
	
	getElem: function(idSuffix) {
		return document.getElementById(this.idRoot + "_" + idSuffix);
	}
	,
	getIndexedElem: function(idSuffix, index) {
		return document.getElementById(this.idRoot + "_" + idSuffix + "_" + index);
	}
	,
	showElem: function(idSuffix) {
		var elem = this.getElem(idSuffix);
		if (elem !== null) {elem.style.display = "";}
	}
	,
	flattenDaapiItem: function(daapiItem) {
		var flatData = {};
		for (var child in daapiItem) {
			if (daapiItem[child] === null) {
				flatData[child] = null;
			} else if (typeof daapiItem[child] != "object") {
				//scalar value
				flatData[child] = daapiItem[child];
			} else {
				//non-scalar, so recurse
				if (typeof daapiItem[child].join == "function" && typeof daapiItem[child].length == "number") {
					//we're going to assume it's an array and just assign it
					flatData[child] = daapiItem[child];
				} else {
					//we have an object, so recurse one level and set members that haven't already been set
					for (var grandchild in daapiItem[child]) {
						if (typeof flatData[grandchild] == "undefined") {flatData[grandchild] = daapiItem[child][grandchild];}
					}
				}
			}
		}
		return flatData;
	}
	,
	processTemplate: function(dataObj, template) {
		//finds template variables in the template passed and replaces them *if* the dataObj has a matching defined member
		
		var regex, template, matches, matchIdx, theMatch, varName;
		regex = /\@Nyx\.[^\@]+\@/g;
		
		matches = template.match(regex);
		if (matches !== null) {
			for (matchIdx = 0; matchIdx < matches.length; matchIdx++) {
				theMatch = matches[matchIdx];
				
				//varName = theMatch.substring( theMatch.indexOf(".") + 1 , theMatch.lastIndexOf("@") );
				varName = theMatch.substring(5, theMatch.length - 1);
				
				if (typeof dataObj[varName] != "undefined") {
					template = NYX.util.string.replaceAll(template, theMatch, dataObj[varName]);
				}
			}
		}
		
		// Fixes the http://www.sacbee.com/@Nyx.AvatarPhotoUrl@ replacement
		regex = /http:\/\/www.sacbee.com\/http:/g;
		
		//alert (regex);
		matches = template.match(regex);
		
		if (matches !== null) {
			theMatch = matches[0];
			template = NYX.util.string.replaceAll(template, theMatch, "http:");
		}
		
		return template;
	}
	,
	applyAltClasses: function(itemIndex, template) {
		/* edits the HTML passed in template to apply alternate-row class names on an every-other and every-third basis
		 * 
		 * IMPORTANT -- itemIndex is expected to be 0-based
		 */
		//template must contain Nyx.AlternateClass variable
		var className, templateVariable;
		templateVariable = "@Nyx.AlternateClass@";
		if (template.indexOf(templateVariable) > -1) {
			className = " ";
			if ( (itemIndex + 1) % 2 === 0 ) {
				//every-other style
				className += this.ALT_CLASS_2 + " ";
			}
			if ( (itemIndex + 1) % 3 === 0 ) {
				//every-third style
				className += this.ALT_CLASS_3 + " ";
			}
			template = NYX.util.string.replaceAll(template, templateVariable, className);
		}
		return template;
	}
	,
	showTarget: function(domTarget) {
		//hide the wait message
		var waitingMsgElem = this.getElem(this.DOM_WAITMSG_SUFFIX);
		if (waitingMsgElem !== null) {
			waitingMsgElem.style.display = "none";
		}
		
		//show the target element
		if (domTarget !== null) {
			domTarget.style.display = "";
		}
	}
};

/**
 * MI added functions, these should probably be corraled into an object
 */
function requestPluckUserAvatar() {
	// create a request batch  
	var requestBatch = new RequestBatch();

	// add the userKey to the request
	var currentUser = retrieveSSOCookieUserId();
	if (currentUser !=='' && currentUser !='undefined') {
		currentUser = hex_md5(currentUser);
	}
	var userKey = new UserKey(currentUser);
	requestBatch.AddToRequest(userKey); 

	// initiate the request. The response will be passed to the renderUserAvatar() method.  
	requestBatch.BeginRequest(serverUrl, renderUserAvatar);
}
// writes the user data into a <img> tag on the page
function renderUserAvatar(responseBatch) {
	var user = retrieveSSOCookieUserId();
console.log('user = '+ retrieveSSOCookieUserId());
	if (user === null) {
		// ignore response
	} else {
		// get the user object out of the response  
		var user = responseBatch.Responses[0].User;
	//	document.getElementById("avatar").src = user.AvatarPhotoUrl;
		var avatar = document.createElement('a');
		avatar.href = '/personas?plckUserId='+user.UserKey.Key+'&insiteUserId='+user.UserKey.Key;
		var genericAvatar = document.getElementById("avatar");
		var newImg = genericAvatar.cloneNode();
		newImg.src = user.AvatarPhotoUrl;
		avatar.appendChild(newImg);
		genericAvatar.replaceChild(avatar,genericAvatar);
	}
}
/**
 * @fileOverview
 * Tools related to persona (DAAPI User object) output.
 * 
 * @author Glen Ford (glenford [at] yahoo.com)
 * @namespace NYX
 */

NYX.CurrentUser = function(idRoot) {
	//do some inheritance
	NYX.util.obj.extend(this, NYX.TemplateTool);
	
	//required, but don't necessarily have to be set in the constructor call
	this.idRoot = idRoot;
	this.userTemplate  = "";	//for a logged-in user
	this.guestTemplate = "";	//for a guest user
	
	this.prepareData = function(user) {
		//cleans up a data item; override this as needed to add/subtract post-processing of the flattened data object
		
		//"flatten" 
		var dataObj = this.flattenDaapiItem(user);
		
		return dataObj;
	};
	
	this.writeGui = function(user) {
		//console.dir(user); return false;
		
		var flatUser, targetElem;
		flatUser = this.prepareData(user);
		targetElem = this.getElem(this.DOM_TARGET_SUFFIX);

		if (targetElem !== null) {
			//process the GUI
			this.initGui(targetElem);
			if (user.UserTier.toLowerCase() == "anonymous" || gSiteLife.mi.userLoggedIn() == false) {
				targetElem.innerHTML = this.processTemplate(flatUser, this.guestTemplate);
			} else {
				//logged-in user
				targetElem.innerHTML = this.processTemplate(flatUser, this.userTemplate);
			}
			this.finishGui(targetElem);

			//show the content and hide the wait message/spinner
			this.showTarget(targetElem);
		}
	};
	
	//these methods are empty; they are called during the GUI-writing process to let you add effects/features, and have access to all the instance members
	this.initGui = function(domTarget) {
		//override this method as needed to add your own GUI customizations
		return true;
	};
	this.finishGui = function(domTarget) {
		//override this method as needed to add your own GUI customizations
		return true;
	};
};

