//////////////////////////////////////////////////////////////////////////
// Microsoft ACDM Widget Script Library
//////////////////////////////////////////////////////////////////////////

/*global ActiveXObject, DOMParser, XSLTProcessor, RegExp, String, document */

///Namespace - NL
var NL;

//NL.NAME = "NL"; 

// *****************************************************************
// Create new NL widget object 
// PID - PartnerID (int)
// WID - Widget ID (int)
// MID - Market ID (int)
// ElemID - ID of DOM element in whic to render NewsLetter
// isExpanded - optionally specifies if decriptions should be shown
//		defaults to false		
// *****************************************************************
function NLWidget(PID, WID, MID, ElemID, isExpanded)
{
    this[NL.PartnerParam] = PID;
    this[NL.WidgetParam] = WID;
    this[NL.MarketParam] = MID;
    this.ElemID = ElemID;
	this.isExpanded = isExpanded;
}

// *****************************************************************
// Create new NL widget object 
// NLList - CSV List of NewsLetters (List<int>)
// WID - Widget ID (int)
// PID - Partner ID (int)
// MID - Market ID (int)
// ElemID - ID of DOM element in whic to render NewsLetter
// isExpanded - optionally specifies if decriptions should be shown
//		defaults to false		
// *****************************************************************
function NLListWidget(NLList, MID, PID, ElemID, isExpanded)
{
    this.NLList = NLList;
    this.MID = MID;
    this.PID = PID;
    this.ElemID = ElemID;
	this.isExpanded = isExpanded;
}

// *****************************************************************
// Singleton NL object wraps widget rendering Logic
// Exposed methods
// render() - Renders widget
// scriptCallback() - Invoked on paratner data file load completion
// submitForm() - on Subscribe button\Learn more link click
// All other metods\poperties are private
// *****************************************************************
NL= new function NLWidgetLibrary ()
{

	// *****************************************************************
	// Global variable declarations
	// *****************************************************************
	this.PartnerParam = "PID";
	this.WidgetParam = "WID";
	this.MarketParam = "MID";
	this.NLListParam = "NLList";	

	// *****************************************************************
	// Internal variable declarations
	// *****************************************************************
	var formID = "newslettersignup";
	var acdmRoot = "http://newsletters.msn.com";
	var acdmLocalRoot = "http://acdm-newsletters.msn.com";
	var acdmINTRoot = "http://newsletters.msn-int.com";	
	//at present we dont have a PPE env so redirect to INT
	var acdmPPERoot = "http://newsletters.msn-int.com";
	var wHost = window.location.hostname.toLowerCase();
	// specifies if widget rendering Locally (INT environments) 
	// used to switch web server host and enable tracing
	// if acdm default to acdm-newsletter else use -int or -ppe
	host = acdmRoot; //default
	host = ((wHost.indexOf('acdm-')!==-1)? acdmLocalRoot  : 					
				((wHost.indexOf('sandblu')!==-1) ? acdmINTRoot : 
				((wHost.indexOf('-int')!==-1) ? acdmINTRoot : 
				((wHost.indexOf('-ppe')!==-1) ? acdmPPERoot : acdmRoot))));
	//specify all in lower case for URL checking
	var insiteURL = host + "/insite2.aspx";
	var splashURL = host + "/splash.aspx";
	var thanksURL = host + "/thankyou.aspx";
	var errorLogURL = host + "/log.ashx";
	var qsParams = "{6}?{0}={1}&amp;MID={2}&amp;WID={3}&amp;SNID={4}&amp;RU={5}";
	var xmlProperty = "P{0}xml";
	var partnerDataFile = host  + "/PData/P{0}_{1}.data?PID={0}&amp;MID={1}";
	var nllistDataFile = host + "/PData/P{0}_{1}.data?XID={0}&amp;MID={1}&amp;NLList={2}&amp;PID={3}";
	var baseXSLString;
	var xTag="<?xml version =\"1.0\" ?>";
	var wrSt="<partner>";
	var wrEnd="</partner>";
	var xWidgetExTag = "/RT/WidgetData/Widgets/Widget[@Id='{0}']/ExtendedProperties/{1}";
	var hashMod = 1000000;
	var numRegex = /^\d*$/;
	//widget xml id (used for callback when the data file loads)
	// - for NL List widget it is a computed hash
	// - for Partner widget it the PID
	// this was added to detangle NLList request from having a PID\MID depenedency
	// if its use needs to be extended later
	var xid = "_xid"; 
	var init = "_init"; 
	//widget isRendered
	var render = "_render";
	var isLogEnabled = (((host !== acdmRoot) && typeof(console)=='object' && console.log) ? true : false); //ON local + FF;
	this.NLXMLData = (typeof(this.NLXMLData)!=='object' ? {} : this.NLXMLData );



	// *****************************************************************
	// Set the newsletter header
	// title set the innerHTML property of the widget Header div any
	// valid markup is displayed
	// *****************************************************************
	 this.SetHeader = function(w, title)
	{
		if(w && w[NL.WidgetParam])
		{
			var head = document.getElementById(String.format('nlhead{0}',w[NL.WidgetParam]));
			if(head)
			{
				head.innerHTML = title;
			}
		}
	 };


	// *****************************************************************
	// Array of Widgets waiting for Partner data file to load
	// *****************************************************************
	this.Queue={};


	// *****************************************************************
	// Array of Widgets waiting for Partner data file to load
	// *****************************************************************
	function initWidget(w) 
	{
		//verify if partner id is set else check for NL List and generate hash
		if("undefined" === w[NL.PartnerParam] && "undefined" === w["NL.NLListParam"])
		{
            //invalid call either PID or NLList must be present
			logMessage("Widget Init FAILED");
			return false; 
		}
		else if(IsPreviewRequest(w))
		{
		    //Generate XmL id hash from List
			var nlIntString = w[NL.NLListParam].replace(/,/g,'');
			if(numRegex.test(nlIntString)){
			    w[xid] = nlIntString % hashMod; 	
				//for list widgets the id = -1
				w[NL.WidgetParam] = -1; 				
			}else{
				//invalid nl list
				logMessage("Widget Init FAILED");
				return false;
            }			
		}else{
				w[xid] = w[NL.PartnerParam];		
		}
		var outerDiv = document.getElementById(w.ElemID);
		if(outerDiv)
		{
			//store original display (inline\block)
			w.OuterDivStyle = outerDiv.style.display;
			//hide widget wrapper will be enabled if data is present
			outerDiv.style.display = 'none';
		}else{
			logMessage("Cannot find Element -" + w.ElemID);
			return false;
		}		
		w[init] = true;
		logMessage("Widget Initialised successfully");
		logObject(w);
		return true;
	}


	// *****************************************************************
	// Public wrapper to render widget
	// w - widget object 
	// callBack - function from the page that will be invoked after render
	// *****************************************************************
	this.render = function (w, callBack)
	{    
		try
		{
			if(isLogEnabled && !w[init])
			{
				//startTrace();
			}
			//Validate properites set on Widget
			if(IsPreviewRequest(w))
			{
				//list widget
				if(!isValidInt(w[NL.MarketParam]) || !isValidIntList(w[NL.NLListParam]))
				{
					logMessage("Invalid params to widget", true);
					logObject(w);
					return false;
				}
			}else{
				if(!isValidInt(w[NL.PartnerParam], w[NL.WidgetParam], w[NL.MarketParam]))
				{
					logMessage("Invalid params to widget", true);
					logObject(w);
					return false;	
				}			
			}
			if(callBack)
			{
				this.callBack = callBack;
			}
			var isRenderComplete = renderWidget(w);			
			if(isLogEnabled && isRenderComplete)
			{
				//endTrace();
			}
		}	
		catch (e)
		{	
			handleError(e, w);
		}			
	};

    function IsPreviewRequest(w)
    {
        return ("undefined" !== w[NL.NLListParam] && w[NL.NLListParam] && w[NL.NLListParam] !== "");
    }


	// *****************************************************************
	// On error emits a beacon than calls log.ashx to log the error data
	// *****************************************************************
	function handleError(e, w)
	{
		try
		{
			var msg = "ERROR rendering \nMessage:\n\t"
			msg = msg + ((e && e.message) ? e.message : "Unspecified Error ") 
					+ ( (e.stack ? "\nStack:\n\t" +e.stack : "" )) ;
			logMessage(msg, true);
			var img = document.createElement("img");
			//Get Widget params
			var log = String.format("?{0}={1}&{2}={3}&{4}={5}", NL.WidgetParam, w[NL.WidgetParam], 
				NL.PartnerParam, w[NL.PartnerParam], NL.MarketParam, w[NL.MarketParam]);  
			var log= log 
			+ "&src=Widget"
			+ "&LOC=" + encodeURIComponent(window.location.href)
			+ "&UA=" + encodeURIComponent(navigator.userAgent.toLowerCase())
			+ ((e && e.message)? "&ERR=" + encodeURIComponent(e.message) : "")
			+ ((e && e.stack)? "&ST=" + encodeURIComponent(e.stack) : "");
			img.src = errorLogURL + log;	
			document.getElementById(w.ElemID).appendChild(img);
		}
		catch (e)
		{
			//throw e;
		}
	}


	function isValidIntList(o)
	{
		if(o)
		{
			o = o.replace(/,/g,'');
			return numRegex.test(o);
		}
	}

	// *****************************************************************
	// Verifies if the arguments passed to it are valid integers
	// *****************************************************************
	function isValidInt(o)
	{
		var l = arguments.length-1;
		for (var i = 0; i <= l; i++)
		{
			if(!arguments[i] || !numRegex.test(arguments[i]))
			{
				return false;
			}
		}
		return true;
	}


	// *****************************************************************
	// Checks and loads the required Partner script if needed
	// Transform the XML based on set style and 
	// redners in the specified element
	// *****************************************************************       
	function renderWidget(w){    	    
		if(!w[init]){
			var valid = initWidget(w);
			if(!valid){
				return;
			}	
		}
		var wid = w[xid];
		var property = String.format(xmlProperty, wid);	
	    if(!NL.NLXMLData || (undefined === NL.NLXMLData[property]))
		{                
			// if Partner Data File not loaded on Client 
			// write script tag to fetch data
		    var script=document.createElement("script");    
			script.type = "text/javascript";
			if(undefined !== w[NL.NLListParam]){
				script.src = encodeURI(String.format(nllistDataFile, wid, w[NL.MarketParam], w[NL.NLListParam], w[NL.PartnerParam]));             
			}else{
				script.src = encodeURI(String.format(partnerDataFile, w[NL.PartnerParam], w[NL.MarketParam]));             
			}
			script.src = script.src.replace(/&amp;/gi,'&');
			NL.Queue[wid] = w;
		    document.getElementsByTagName("head")[0].appendChild(script);            
			logMessage("Begin Get Partner File --" + script.src + "\nWaiting...");
	    }else{	        
			// Partner Data file loaded
			// transform xml and render		
			logMessage("Partner File Recieved \nBegin XML Parse");
			var xmlData = NL.NLXMLData[property];
			var xmldoc = parse(xmlData);  		
			var hover = (w.Hover !== undefined ? w.Hover :
				getXMLValue(xmldoc, String.format(xWidgetExTag, w[NL.WidgetParam], "Hover")));
			var form = createForm(w);        		
			form.innerHTML = transformXml(getStyleSheet(w, hover),xmldoc);                    		
			logMessage("XML transformed successfully");
			verifyRender(form, w);
			document.getElementById(w.ElemID).appendChild(form);		
			//Indicate CSS has not been linked;If added will be set to true below
			w.IsCSSLinked = false;			
			//If spalsh page render - splash text and append CSS URLs			
			if(isSplashPage())
			{
				var txt = getXMLValue(xmldoc, String.format(xWidgetExTag, 
							w[NL.WidgetParam], "SplashText"));
				if(setSplashText)
				{
					setSplashText(txt);
				}	
				var css = getXMLValue(xmldoc, String.format(xWidgetExTag, 
							w[NL.WidgetParam], "CssUrl"));							
				if(css && addCSSURL)
				{
					addCSSURL(css);
					//Set Rendered Widget
					NL.RenderedWidget = w;					
					//Incidcate that a CSS URL is to be appeneded. This trigger the common.js
					//to wait for SplashPreview image load
					w.IsCSSLinked = true;										
				}
				//Add CBID to Login Link
				var link;
				if(authLinkID)
				{
					link = document.getElementById(authLinkID);
					if((typeof(partnerCbid) !== "undefined") && partnerCbid && link && link.innerHTML.indexOf('In') !== -1)
					{
						link.href = link.href + "&cbid=" + partnerCbid;						
					}
				}
			}
			xmldoc = null;
			if(w.ForceDefaultStyles === true)
			{
				writeStyles(defaultStyles);		
			}
			if(NL.callBack)
			{
				NL.callBack.call();
			}
			logMessage("Render Successful");
			return true;
		}    
		return false;
	}
 

	// *****************************************************************
	// Verifies widget makup before attaching to DOM
	//  - If no newsletters and no crossSells turn off the widget
	//  - IF no newsletters show only CrossSells but no CrossSellHeader
	//  - IF no crosssells hide CrossSellHeader
	//  - Added IF SNID present and on Splash then check NLs
	// *****************************************************************
	function verifyRender(form, w)
	{
		var nlFound=false, csFound = false;
		var wrapper, csHeader, outerDivStyle; 
		var outerDiv = document.getElementById(w.ElemID);
		//find wrapper and header tags 
		var divs = form.getElementsByTagName('div');
		for(var i=0;i<divs.length;i++){
			if(divs[i] && divs[i].id)
			{
				if(divs[i].id.indexOf("nlPr") === 0){
					wrapper = divs[i];
				}else if(divs[i].id.indexOf("nlCrossSellHead") === 0)
				{
					csHeader = divs[i];
					break;
				}
			}
		}
		var isSplash = isSplashPage();
		var snid = (undefined === getParam("SNID") ? "" : getParam("SNID")).split(',');
		var nlList = form.getElementsByTagName('input');
		for(i=0;i<=nlList.length;i++){
			if(nlList[i] &&  nlList[i].id)
			{				
				if(nlList[i].id.indexOf('nlCbCS') === 0)
				{
					csFound = true;					
				}	 
				else if(nlList[i].id.indexOf('nlCb') === 0)			
				{
					nlFound = true;

				}
				//If user Selected any NL and hit Learn more
				// turn them on
				if(isSplash && snid.length > 0)
				{
					var nlid = nlList[i].id.replace(/nlCbCS/gi,'');
					nlid = nlid.replace(/nlCb/gi,'');
					for(j=0;j<=snid.length;j++)
					{
						if(snid[j] === nlid)
						{					
							nlList[i].checked = true;
							nlList[i].setAttribute("checked",true);
							nlList[i].defaultChecked = true;
							break;
						}						
					}
				}
			}
		}	
		if(nlFound || csFound)
		{
			outerDiv.style.display = w.OuterDivStyle; 
		}
		if(!nlFound && !csFound && wrapper && wrapper.style)
		{
			logMessage("Partner File Contains no NL or CS data for widget -- HIDING WIDGET" , true);
			wrapper.style.display = 'none'; //no NL and no CS			
		}	
		if(!csFound  && csHeader && csHeader.style)
		{
			logMessage("Partner File Contains no CS data for widget -- HIDING CROSSSells" , true);
			csHeader.style.display = 'none'; 
		}
	}


	// *****************************************************************
	// Callback wrapper (from Partner Data Fileload)     
	// Invokes render for associated widget
	// *****************************************************************
	this.scriptCallback = function(ID){
		var w = this.Queue[ID];  
		if(w && (undefined !== this.NLXMLData[String.format(xmlProperty, w[xid])]))
		{	     
			this.render(w);
			delete this.Queue[ID];
		}
		else
		{
			logMessage("Queueing Callback Error ID=" + ID, true );
			logObject(this.Queue);
		}
	}; 


	// *****************************************************************
	// Creates form element within which the 
	// Newsletters are rendered
	// *****************************************************************
	function createForm(w){
		var form=document.createElement("form",true);        
	    form.id=formID;
		//add widget properties to be passed 
		var qparam = ((undefined === w[NL.NLListParam]) 
						  ? NL.PartnerParam : NL.NLListParam); //Parameter PID or NLlist
		var ru;
		if(isSplashPage() || isThankYouPage())
		{
			//on splash page preserve RU
			ru = getParam("RU");
		}else{
			ru = encodeURIComponent(window.location.href);
		}
		// -SNIDs and URL (splash/insite) will be inserted onSubmit
		form.action = String.format(qsParams, qparam, encodeURIComponent(w[qparam])
					, w[NL.MarketParam] , w[NL.WidgetParam], "{0}"
					, ru , insiteURL);
		logMessage("FORM RU = " + form.action);
		return form;
	}


	// *****************************************************************
	// Handles form submit
	// If no Newsletters are selected or Learn more is clicked redirect
	// to the Splash page. In other cases redirect to the Insite page
	// *****************************************************************
	this.submitForm = function(splash)
	{
	    var form=document.getElementById(formID); 
		form.action = form.action.replace(/&amp;/gi,'&');
		var nlList = form.getElementsByTagName('input');
		var snid = "";
		var url= insiteURL;
		for(var i=0;i<=nlList.length;i++){
			if(nlList[i] && nlList[i].checked){
				snid = snid + nlList[i].id + ",";
			}
		}
		if(snid.length>1)
		{
			snid = snid.substring(0, snid.length-1);
			snid = snid.replace(/nlCbCS/gi, '');//clean id strings
			snid = snid.replace(/nlCb/gi, ''); 
		}else{
			//If already on Splash Page display alert
			if (isSplashPage() || isThankYouPage())
			{
				alert(NL.AlertMsg);
				return false;
			}	
			splash= true;
		}
		if(splash){
			form.action = form.action.replace(insiteURL, splashURL) ; 		
		}
		form.action = String.format(form.action, snid);
		window.location.href = form.action;
	};
	

	// *****************************************************************
	// Returns if the widget is currently rendering on the Splash Page
	// *****************************************************************
	function isSplashPage()
	{
		return !(window.location.href.toString().toLowerCase().indexOf(splashURL) === -1);		
	}

	// *****************************************************************
	// Returns if the widget is currently rendering on the ThankYou Page
	// *****************************************************************
	function isThankYouPage()
	{
		return !(window.location.href.toString().toLowerCase().indexOf(thanksURL) === -1);
	}

	// *****************************************************************
	// Gets SNID array id present
	// *****************************************************************
	function getSNID()
	{
		var	snid = getParam("SNID");
		if(snid)
		{
			snid = snid.replace(/nlCb/gi,'');
			snid = snid.replace(/nlCbCS/gi,'');			
			return snid.split(',');			
		}
	
	}

	// *****************************************************************
	// Get Param value for browser href
	// *****************************************************************
	function getParam(name)
	{
		var qs = window.location.search.substring(1);
		var args = qs.split('&') ;	
	    for (var i=0;i<args.length;i++) 
		{
			var val = args[i].split('=');
			if(val[0]===name)
			{
				return val[1];
			}
		}
	}
	
	// *****************************************************************
	// Add the styles to the DOM head
	// input string defines the styles and should be in valid CSS format
	// E.g
	//	.NLWidget
	//	{	
	// 		width: 520px;
	//	 	border: 1px solid #B3C58F;  
	//	}
	//	DIV.nldescription
	//	 {	
	// 		 margin-left: 24px;	
	//	}
	// ...
	// Do not wrap in style tag
	// *****************************************************************
	function writeStyles(rule) {    
		logMessage("Forcing default styles on Widget");
		//trim text
		rule = rule.replace(/\n/g, '').replace(/^\s*|\s*$/g,'');
		if(document.createStyleSheet)
		{
			//IE
			var styleSheetObj=document.createStyleSheet();		
			// get the DOM node of the style sheet object, set the type
			styleObj=styleSheetObj.owningElement || styleSheetObj.ownerNode;
			styleObj.setAttribute("type","text/css");
			// rule is a string with CSS markup, see above.
			// Split the String in an array, as each rule has to be inserted individually
			ruleArray = rule.split("}");
			// insert every rule
			for (var i=0; i < ruleArray.length - 1; i++) {
			    var currentRule = (ruleArray[i]);
			    // split the remaining String into pairs "name" and "value",
			    var nameValueArray = currentRule.split("{");
			    var name = nameValueArray[0];
				var value = nameValueArray[1];
				// add the rule to the style sheet node  
				styleSheetObj.addRule(name,value);
			}
		}else{
			//FF
			var styleObj=document.createElement("style");
			styleObj.setAttribute("type","text/css");
			styleObj.appendChild(document.createTextNode(rule));	
			document.getElementsByTagName("head")[0].appendChild(styleObj);				
		}
	}


	// *****************************************************************
	// Write debug Logging messages to console
	// Current implementaion will use FF console (n/a on IE)
	// TODO - need to disable for Retails builds
	// *****************************************************************
	function logMessage(msg, isError, isWarning)
	{
		if(isLogEnabled)
		{
			if(isWarning)
			{
				console.warn(msg);
			}else if(isError)
			{
				console.error(msg);
			}else{
				console.log(msg);
			}
		}		 
	}

	function logObject(o)
	{
		if(isLogEnabled && console.dir)
		{
			console.dir(o);
		}	
	}

	function startTrace()
	{
		console.profile();
	}

	function endTrace()		
	{
		console.profileEnd();
		logMessage("Trace Stack Info");
		console.trace();
	}

	// *****************************************************************
	// XML Helpers\defs
	// *****************************************************************
	
	// *****************************************************************
	// Transforms the xml node using the stylesheet specifed
	// returns string of the HTML nodes
	// *****************************************************************
	function transformXml(xslDOM, xmlDOM){
	      if (typeof XSLTProcessor !== "undefined") {        
	        //Mozilla based browsers        
	        var processor = new XSLTProcessor();                
	        processor.importStylesheet(xslDOM);         
	        return processor.transformToDocument(xmlDOM).documentElement.innerHTML;
	      }else{
	        //IE 5 +        
	        return (xmlDOM.transformNode(xslDOM));     
	      }
	}
	

	// *****************************************************************
	//  Creates an XML doc parser 
	// *****************************************************************
	function newXMLDocument() {    
	    var rootTagName = "";
	    var namespaceURL = "";
	    if (document.implementation && document.implementation.createDocument) {
	        //  W3C Moz
	        return document.implementation.createDocument(namespaceURL,
	                                             rootTagName, null);
	    }
	    else { // IE 
	            return new ActiveXObject("MSXML2.DOMDocument"); 
	    }            
	}
	
	
	// *****************************************************************
	// Parses input Xml string 
	// Returns Xml document object
	// *****************************************************************
	  function parse(xml){
	    if (typeof DOMParser !== "undefined") {
	        // Mozilla
	        return (new DOMParser()).parseFromString(xml, "application/xml");
	    }
	    else if (typeof ActiveXObject !== "undefined") {
	        // IE
	        var doc = newXMLDocument();  
	        doc.loadXML(xml);                 
	        return doc;                   
	    } 
	}
	  
	  
	// *****************************************************************
	// Uses the embedded XSL string
	// Optionally can use string replacement to get
	// XSL for the required form factor (descriptions/Splash mode)  
	// returns parsed DOM of the stylesheet
	// *****************************************************************
	  function getStyleSheet(w, hoverEnable){	  
		var msg = "StyleSheet Logic";
		var header= false,  description = false, footer = false, hover = false;
		//these are changes only for Thankyoucrosells
		var newsletters = true, crossSellNode="CrossSellNewsletterIds", nameClass=""; 
		if(isSplashPage())
		{
			description = true;
			msg = msg + "\n\twidget on Splash Page - Show Descriptions + Disable Hover";			
		}
		else if(w.ShowExtraCrossSells)
		{
			//If extra CrossSells - hide regular NLs\CS; show only CrossSells
			crossSellNode = "ThankYouCrossSellNewsletterIds";			
			newsletters = false; //disable newsletters
			if(hoverEnable !== 'false' && hoverEnable!== false)
			{
				hover = true;	
				msg = msg + "\n\twidget Enable hover";
			}		
		}
		else
		{
			if (w.isExpanded)
			{
				description = true;
				msg = msg + "\n\tw.isExpanded is True -Show Descriptions ";
			}
			else if(hoverEnable !== 'false' && hoverEnable!== false)
			{
				hover = true;
				msg = msg + "\n\twidget Enable hover";
			}		
			header = true;
			footer = true;
			msg = msg + "\n\twidget Not on Splash\ThankYou - Show Header + Footer";	
		}
		if(description)
		{
			nameClass = "em";
		}
		logMessage(msg);
		var str = String.format(baseXSL, w[NL.WidgetParam], header, footer,
						newsletters, crossSellNode, description, hover, nameClass);
	    return parse(str);
	 }

	 
	// *****************************************************************
	// Retrieves the value of the XPath expression from the XML Document
	// *****************************************************************
	  function getXMLValue(context, xpath){	  
		try {
			var result;
			if (document.createExpression)
			{	
				//FF
				var xpe = new XPathEvaluator();
				var nsResolver = xpe.createNSResolver(context.ownerDocument === null ?
							    context.documentElement : context.ownerDocument.documentElement);
				result = xpe.evaluate(xpath, context, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
				if(result.singleNodeValue && result.singleNodeValue.textContent)
				{
					return result.singleNodeValue.textContent;
				}
			}else {
				//IE
				    var doc = context.ownerDocument;
					if (doc == null)
					{ 
						doc = context;
					}
		            doc.setProperty("SelectionLanguage", "XPath");        
		            if (context == doc)
					{
						context = doc.documentElement;
					}
			        result = context.selectSingleNode(xpath);  
					if(result && result.text)
					{
						return result.text;
					}
			}
			logMessage("Unable to find value" + xpath, false, true);
			return null;
		}catch(e) {    
			var msg = "Error evaluating XPath-"+ xpath + "\nError:\n\t" + e.message;
			logMessage(msg, false, true);
			return null;
		}	    
	  }
	
	 
	var defaultStyles ="\
	.newsletter\
	{\
		 font-family: Tahoma;\
		font-size: 93%;\
	}\
	.newsletter.nltitle\
	{\
		font-weight: bold;\
		background-color: #ECF3E1;\
		padding: 2px 6px;\
		border-bottom:	1px solid #B3C58F\
	}\
	.newsletter.nlsubhead\
	{\
		width: 95%;\
		background-color: #EFF5E7;\
		color: Gray;\
		font-weight: bold;\
	}\
	.newsletter.nlchild\
	{\
		margin: 8px 5px 0px;\
	}\
	.NLWidget\
	{\
		width: 200px;\
		border: 1px solid #B3C58F;\
	}\
	DIV.nldescription\
	{\
		 margin-left: 24px;\
	}\
	#newsletterbody INPUT\
	{\
		margin-right: 5px;\
	}"


	// **********************************************************************************
	// Embedded XSL and variations
	// DO NOT CHANGE EMELENT IDS and CLASS defs 
	// String format ID/Element/Desc
	// 0	w[NL.WidgetParam]	- WidgetID used for Xpath node select 
	// 1	header				- XSL to display header or hide (in Splash\Thankyou CS)
	// 2	footer				- Learn more or blank
	// 3	Newsletter			- XSL to display or Hide Newsletters (in Thankyou CS)
	// 4	crossSellNode		- Regular CrossSell or ExtraCrossSells	
	// 5	description			- Either Hover version or text version
	// 6	hover				- Mouse events if Hover present or blank
	// 7    NLNameClass			- IF the description is present the NL Name class attribute
	//							  is modified for making bold etc 
	// **********************************************************************************
	baseXSL = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\
	<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
	  <xsl:output method=\"html\" />\
	  <xsl:key name=\"nlKey\" match=\"Newsletter\" use=\"@NewsletterId\"/>\
	  <xsl:variable name=\"nlLookup\" select=\"/RT/WidgetData/Newsletters/Newsletter\"/>\
	  <xsl:variable name=\"gid\" select=\"/RT/WidgetData/Newsletters/Newsletter\"/>\
	  <xsl:variable name=\"csCount\" select=\"0\"/>\
	  <xsl:variable name=\"showHeader\" select=\"'{1}'\"/>\
	  <xsl:variable name=\"showFooter\" select=\"'{2}'\"/>\
	  <xsl:variable name=\"showNewsLetters\" select=\"'{3}'\"/>\
	  <xsl:variable name=\"showDescription\" select=\"'{5}'\"/>\
	  <xsl:variable name=\"showHover\" select=\"'{6}'\"/>\
	  <xsl:template match=\"/\">\
		<div>\
		<div class=\"newsletter nlwrapper\" id=\"nlPr{0}\">\
			<xsl:if test=\"$showHeader='true'\">\
				<div class=\"newsletter nltitle\" id=\"nlHead{0}\">\
					<xsl:value-of select=\"/RT/Strings/WidgetHeader\"/>\
			    </div>\
			</xsl:if>\
			<div class=\"newsletter nlbody\" id=\"nlBody{0}\">\
		  		<xsl:if test=\"$showNewsLetters='true'\">\
					<div class=\"newsletter nllist nlchild\" id=\"nlNewsletters{0}\">\
						<xsl:apply-templates select=\"/RT/WidgetData/Widgets/Widget[@Id='{0}']/NewsletterIds/NL\"/>\
					</div>\
				</xsl:if>\
				<div class=\"newsletter nlsubhead nlchild\" id=\"nlCrossSellHead{0}\">\
					<xsl:value-of select=\"/RT/Strings/CrossSellHeader\"/>\
				</div>\
				<div class=\"newsletter nllist nlchild\" id=\"nlCrossSellNewsletters{0}\">\
					<xsl:apply-templates select=\"/RT/WidgetData/Widgets/Widget[@Id='{0}']/{4}/NL\"/>\
				</div>\
				<div class=\"newsletter nlsubmitwrap nlchild\" id=\"nlFooter{0}\">\
					<input type=\"submit\" id=\"nlSubmit\"  class=\"newsletter button nlsubmitbtn\" onclick=\"NL.submitForm();return false;\" >\
						<xsl:attribute name=\"value\">\
			              <xsl:value-of select=\"/RT/Strings/SubmitBtn\" />\
						</xsl:attribute>\
					</input>\
				</div>\
				<div id=\"nlZoomObj\" style=\"position:absolute; top:100px; left:100px; height:0px; width:0px;\"></div>\
				<xsl:if test=\"$showFooter='true'\">\
					<div class=\"newsletter nlfooter nlchild\">\
						<span>\
							<a id=\"NLLearnMore0\" href=\"javascript:NL.submitForm(true)\">\
								<xsl:value-of select=\"/RT/Strings/LearnMore\"/>\
							</a>\
						</span>\
					</div>\
				</xsl:if>\
			</div>\
			<xsl:apply-templates select=\"/RT/Strings/AlertMessage\"/>\
		</div>\
		</div>\
	  </xsl:template>\
     <xsl:template match=\"/RT/Strings/AlertMessage\">\
	   <script type='text/javascript'>\
		  <![CDATA[NL.AlertMsg=\"]]><xsl:value-of select=\".\" /><![CDATA[\";]]>\
		</script>\
	  </xsl:template>\
	  <xsl:template match=\"/RT/WidgetData/Widgets/Widget/CrossSellNewsletterIds/NL\">\
		<table style=\"border-spacing: 0px; padding: 0px;\" class=\"tblCSNewsletters\">\
			<xsl:call-template name=\"NLdisplay\">\
				<xsl:with-param name=\"NL\" select=\"key('nlKey',.)\"/>\
				<xsl:with-param name=\"chkID\" select=\"'nlCbCS'\"/>\
			</xsl:call-template>\
		  </table>\
	  </xsl:template>\
	  <xsl:template match=\"/RT/WidgetData/Widgets/Widget/ThankYouCrossSellNewsletterIds/NL\">\
		<table style=\"border-spacing: 0px; padding: 0px;\" class=\"tblCSNewsletters\">\
		  <xsl:call-template name=\"NLdisplay\">\
			<xsl:with-param name=\"NL\" select=\"key('nlKey',.)\"/>\
			<xsl:with-param name=\"chkID\" select=\"'nlCbCS'\"/>\
			</xsl:call-template>\
		  </table>\
	  </xsl:template>\
	  <xsl:template match=\"/RT/WidgetData/Widgets/Widget/NewsletterIds/NL\">\
		<table style=\"border-spacing: 0px; padding: 0px 0px 5px 0px;\" class=\"tblNewsletters\">\
			<xsl:call-template name=\"NLdisplay\"> \
				<xsl:with-param name=\"NL\" select=\"key('nlKey',.)\"/>\
				<xsl:with-param name=\"chkID\" select=\"'nlCb'\"/>\
			</xsl:call-template>\
		  </table>\
	  </xsl:template>\
	  <xsl:template name=\"NLdisplay\">\
		<xsl:param name=\"NL\"/>\
		<xsl:param name=\"chkID\"/>\
		<xsl:variable name=\"nlid\" select=\"$NL/@NewsletterId\"/>\
	   <xsl:if test=\"$NL/*\">\
	   	<TR CLASS=\"newsletter NlElement\" valign=\"top\">\
				<TD>\
					<input type=\"checkbox\" name=\"nlCBList\">\
						<xsl:attribute name=\"id\"><xsl:value-of select=\"$chkID\"/><xsl:value-of select=\"$nlid\" />\
						</xsl:attribute>\
				    </input>\
				</TD>\
				<TD style=\"padding-top: 3px\">\
					<label class=\"newsletter nlname {7}\">\
						<xsl:if test=\"$showHover='true'\">\
							<xsl:attribute name=\"onmousemove\">\
								<xsl:value-of select=\"'NL.Tooltip.showTooltip(event,this);'\"/>\
			    		    </xsl:attribute>\
							<xsl:attribute name=\"onmouseout\">\
								<xsl:value-of select=\"'NL.Tooltip.hideTooltip(this)'\"/>\
						    </xsl:attribute>\
						</xsl:if>\
						<xsl:attribute name=\"for\">\
							<xsl:value-of select=\"$chkID\"/><xsl:value-of select=\"$nlid\" />\
				        </xsl:attribute>\
						<xsl:attribute name=\"id\">nlLbl<xsl:value-of select=\"$nlid\" />\
						</xsl:attribute>\
						<xsl:value-of select=\"$NL/PublicName\"/>\
					</label>\
				</TD>\
			<xsl:if test=\"($NL/BriefDescriptionText) and string-length(normalize-space($NL/BriefDescriptionText)) > 0\">\
				<xsl:choose>\
					<xsl:when test=\"$showDescription ='true'\">\
						<TR class=\"newsletter nldescription\">\
							<TD></TD>\
							<TD>\
								<label>\
									<xsl:attribute name=\"for\"><xsl:value-of select=\"$chkID\"/><xsl:value-of select=\"$nlid\" />\
									</xsl:attribute>\
									<xsl:attribute name=\"id\">nlDesc<xsl:value-of select=\"$nlid\" />\
									</xsl:attribute>\
									<xsl:value-of select=\"$NL/BriefDescriptionText\"/>\
								</label>\
							</TD>\
						</TR>\
					</xsl:when>\
					<xsl:when test=\"$showHover ='true'\">\
					<tr><td colspan='2'>\
						<div class=\"nlHoverWrap\" style=\"width:250px !important; max-width:250px; left: 595px; top: 388px; z-index: 99999; display: none;background-color:#C5C0C0; position:absolute;\">\
							<div class=\"nlHoverDiv\" style=\"background-color:#FFFFFF; height:100%; left:-5px; position:relative; top:-4px; \">\
								<table class=\"nlHoverTbl\" style=\"border:0px none; width:100%\">\
									<tr><td class=\"nlHoverTd\" style=\"border:1px solid #95B9C5; padding:10px; width:100%\">\
										<xsl:value-of select=\"$NL/BriefDescriptionText\"/>\
									</td></tr>\
								</table>\
							</div>\
						</div>\
						</td></tr>\
					</xsl:when>\
				</xsl:choose>\
			</xsl:if>\
		</TR>\
	  </xsl:if>\
	</xsl:template>\
</xsl:stylesheet>"
		
	// *****************************************************************
	// Defines the widget hover styles
	// *****************************************************************
	widgetStyles = "\
	.nlHoverWrap{left: 595px; top: 388px; z-index: 99999; display: none;background-color:#C5C0C0; position:absolute;}\
	.nlHoverDiv{background-color:#FFFFFF; height:100%; left:-5px; position:relative; top:-4px; width:100%;}\
	.nlHoverTbl{border:0px none; width:200px; padding :5px;}\
	.nlHoverTd{border:1px solid #95B9C5; padding:10px;}"

};


	// *****************************************************************
	//  String Helpers\defs
	// *****************************************************************
	String.format = function()
	{    
		if( arguments.length === 0){
		 return null;
	    }
	    var str = arguments[0];
	    for(var i=1;i<arguments.length;i++){   
			str = str.replace(new RegExp('\\{' + (i-1) + '\\}','gm'), arguments[i]);	
	    }
		return str;
	};
	


// *****************************************************************
// TOOLTip
// *****************************************************************
NL.Tooltip = new function TooltipLibrary()
{
    this.documentElement = (document.documentElement.clientWidth==0)?document.body:document.documentElement;

    this.getLocationX = function(event, menuObject)
    {    
        var scrollLeft = (document.all)?this.documentElement.scrollLeft:window.pageXOffset;
        var x = scrollLeft + event.clientX;
        x = this.checkLocationX(event, x ,menuObject);
        return x;
    }

    this.getLocationY= function(event, menuObject)
    {
        var scrollTop = (document.all)?this.documentElement.scrollTop:window.pageYOffset;
        var y = scrollTop + event.clientY;
        y = this.checkLocationY(event, y, menuObject);
        return y;
    }

    this.checkLocationX = function(event, x, menuObject, parentObj)
    {
        var scrollLeft = (document.all)?this.documentElement.scrollLeft:window.pageXOffset;
        var prevX = x;
        if((x + menuObject.scrollWidth) > (this.documentElement.clientWidth + scrollLeft)) 
        {                                                   //20 for scrollbar width
               x -= (menuObject.scrollWidth + 10);
               if(parentObj)
               {
                    x-=parentObj.offsetWidth;
               }
        }
        else
        {
            x += 10;
        }
               
        return (x>0)?x:prevX;
    }
    this.checkLocationY = function(event, y, menuObject, parentObj)
    {    
        var scrollTop = (document.all)?this.documentElement.scrollTop:window.pageYOffset;        
        var prevY = y;        
        if((y + menuObject.scrollHeight) > (this.documentElement.clientHeight + scrollTop))
        {                   //20 for scrollbar width
               y -= (menuObject.scrollHeight + 10);    
               if(parentObj)
                     y-=parentObj.offsetHeight ;      
        }
        else
        {
            y += 10;
        }        
        return (y > 0)? y : prevY;
    }

    this.showTooltip = function(event,o)
    {      
        try
		{
			var toolTip = o.parentNode.parentNode.parentNode.getElementsByTagName('div')[0];
	        if (!toolTip || toolTip ===undefined ||toolTip.innerHTML == '')
	            return;
	        toolTip.width = toolTip.scrollWidth;
	        toolTip.height = toolTip.scrollHeight;
	        var locX = this.getLocationX(event, toolTip);
	        var locY = this.getLocationY(event, toolTip);
	        var zoomObj = document.getElementById("nlZoomObj");
	        if( zoomObj && zoomObj.offsetLeft)
	        {
	            var zoom = 1;
	            zoom = zoomObj.offsetLeft/100; //100 is absolute position of the zoomObj div set in subscription page
	            locX = parseInt(locX/zoom);
	            locY = parseInt(locY/zoom);
	        }
	        toolTip.style.left = locX + 'px';
	        toolTip.style.top  = locY + 'px';
	        toolTip.style.zIndex = 99999;
	        toolTip.style.display = 'inline';
		}catch(e) {    
			var msg = "ERROR  \nMessage:\n\t"
			msg = msg + ((e && e.message) ? e.message : "Unspecified Error ") 
					+ ( (e.stack ? "\nStack:\n\t" +e.stack : "" )) ;
			logMessage(msg, true);
		}	   
    }
        


    this.hideTooltip = function(o)
    {
		try
		{
			var toolTip = o.parentNode.parentNode.parentNode.getElementsByTagName('div')[0];
	        if (toolTip != null)
		    {
			    toolTip.style.display='none';
	        }
		}catch(e) {    
			var msg = "ERROR  \nMessage:\n\t"
			msg = msg + ((e && e.message) ? e.message : "Unspecified Error ") 
					+ ( (e.stack ? "\nStack:\n\t" +e.stack : "" )) ;
			logMessage(msg, true);
		}	   	
    }
}



