/**
@file	utils.js
File that contains utility methods for DOM-style operations
*/
///////////////////////////////////////////////////////////////////////
// BEGIN AdobeProperty
/**
A property class that allows you to Get() and Set() a value and Bind()
that value to DOM elements or arbitrary functions to automagically
have things happen when the property changes.
*/

function AdobeProperty(inValue)
{
	this.value = inValue;
}


AdobeProperty.prototype.Get = function()
{
	return this.value;
}


AdobeProperty.prototype.Set = function(inValue)
{
	if (this.value != inValue)
	{
		this.value = inValue;
		if (this.objects)
		{
			for (var n = 0; n < this.objects.length; n++)
			{
				this._BindNotify(this.objects[n]);
			}
		}
	}
}


AdobeProperty.prototype.Bind = function(inObject, inMethod)
{
	var bRet = false;

	if (inObject)
	{
		var bindRecord = { obj: inObject, method: inMethod };
		if (this._BindNotify(bindRecord))
		{
			if (!this.objects)
				this.objects = new Array();
			this.objects.push(bindRecord);
			
			// Input elements get bi-directional
			if (inObject.nodeType == 1 && (inObject.nodeName == "INPUT" || inObject.nodeName == "SELECT"))
			{
				var thisCB = this;
				// Should preserve any existing onchange...
				inObject.onchange = function() { thisCB.Set(this.value); };
			}
			bRet = true;
		}
	}

	return bRet;
}


AdobeProperty.prototype._BindNotify = function(inBindRecord)
{
	var bRet = false;

	if (!inBindRecord || !inBindRecord.obj)
		return bRet;

	// If the bound method is a function, use it as a filter for the value.
	var targetValue = this.value;
	if ("function" == typeof inBindRecord.method)
		targetValue = inBindRecord.method(this.value);

	switch (typeof inBindRecord.obj)
	{
		case "function":
			// Bound to a function, just call it with the property value
			inBindRecord.obj(targetValue);
			bRet = true;
			break;
		case "string":
			// Bound to a string, set the value
			inBindRecord.obj = targetValue;
			bRet = true;
			break;
		case "object":
			// Bound to an object, then we try a few different things
			// 0. An object with a valid method bound
			if ("string" == typeof inBindRecord.method && "function" == typeof inBindRecord.obj[inBindRecord.method])
			{
				inBindRecord.obj[inBindRecord.method](targetValue);
				bRet = true;
			}
			// 1. An element
			else if (1 == inBindRecord.obj.nodeType)
			{
				// For form fields, set the value
				if ("INPUT" == inBindRecord.obj.nodeName || "SELECT" == inBindRecord.obj.nodeName)
				{
					inBindRecord.obj.value = targetValue;
				}
				// Other elements, replace the content
				else
				{
					while (inBindRecord.obj.lastChild)
						inBindRecord.obj.removeChild(inBindRecord.obj.lastChild);
					inBindRecord.obj.appendChild(document.createTextNode(targetValue));
				}
				bRet = true;
			}
			// 2. An attribute or text node
			else if (2 == inBindRecord.obj.nodeType || 3 == inBindRecord.obj.nodeType)
			{
				inBindRecord.obj.nodeValue = targetValue;
				bRet = true;
			}
			// 3. An object with a Set() method
			else if (inBindRecord.obj.Set && "function" == typeof inBindRecord.obj.Set)
			{
				inBindRecord.obj.Set(targetValue);
				bRet = true;
			}
			// 4. An object with a value property
			else if (undefined != inBindRecord.obj.value && (typeof inBindRecord.obj.value == typeof targetValue))
			{
				inBindRecord.obj.value = targetValue;
				bRet = true;
			}
			// 5. Don't know what to do
			break;
		default:
			// Unhandled type
	}
	return bRet;
}
// END AdobeProperty
///////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////
// BEGIN WizardAlert

/**
Modal alert widget.
*/
function WizardAlert(inContainerProxy)
{
	this.session = inContainerProxy;
	this.title = null;
	this.body = null;
	this.buttons = new Array();

	if (!this.alertTemplate)
	{
		var alertPathArray = new Array(this.session.GetResourcesPath(), "/common/alert/alert.html");
		var alertPath = _concatPaths(alertPathArray, this.session.GetDefaultProperties().platform);
		
		var alertContents = this.session.LoadFile(alertPath);
		this.alertTemplate = alertContents.data;
		
		var cssFileName = "alert";
		var currentLang = getUserInterfaceLanguage(inContainerProxy);
		
		if (null != currentLang)
		{
			if (isLanguageRTL(currentLang)) 
				cssFileName += "_rtl";
		}
		
		// doctor up the root path to the CSS
		var cssURIArray =  new Array(this.session.GetResourcesPath(), "common", "alert", cssFileName);
		var cssURI = "file://" + _concatPaths(cssURIArray, this.session.GetDefaultProperties().platform);

		this.alertTemplate = ExpandTokens(this.alertTemplate, { CSSRoot: cssURI });		
	}

	if (!this.alertTemplate || this.alertTemplate.length <= 0)
	{
		throw "Unable to load alert.html";
	}
}


/**
Run (show) the alert.
*/
WizardAlert.prototype.Run = function()
{
	// Formulate a token map to fill in the alert template.
	var repMap = new Object();

	// Title and body
	if (this.session.localization)
		repMap.AlertWindowTitle = this.session.localization.GetString("locAlertWindowTitle", "Installer Alert");
	else
		repMap.AlertWindowTitle = "Installer Alert";
	repMap.AlertTitle = this.title;
	repMap.AlertBody = this.body;

	// Button text padding
	var buttonBuffer = ""; // they look like spaces, but they are Special

	// Buttons
	repMap.AlertButtons = "";
	for (var bi = 0; bi < this.buttons.length; bi++)
	{
		// Setup standard button attributes
		var attributes = {
			type: "button",
			value: buttonBuffer + this.buttons[bi].label + buttonBuffer,
			onclick: "window.external.UIExitDialog('" + this.buttons[bi].returnCode + "');",
			onfocus: "setFocus(this);"
		};

		var html = "<input";
		var attr = null;

		// Standard attributs
		for (attr in attributes)
		{
			html += " " + attr + "=\"" + attributes[attr] + "\"";
		}

		// User supplied attributes augment or override
		for (attr in this.buttons[bi].attributes)
		{
			html += " " + attr + "=\"" + this.buttons[bi].attributes[attr] + "\"";
		}

		html += "/>";
		repMap.AlertButtons += html;
	}

	return this.session.UIShowModalAlert(ExpandTokens(this.alertTemplate, repMap));
}


/**
Set the title.  This will be automatically wrapped in an <h1> but may contain other HTML markup.
*/
WizardAlert.prototype.SetTitle = function(inTitle)
{
	this.title = inTitle;
}


/**
Set the body.  This will automatically be wrapped in a <div> and should contain appropriate markup, for example <p> tags.
*/
WizardAlert.prototype.SetBody = function(inBody)
{
	this.body = inBody;
}


/**
Add an alert button.  Buttons are displayed left to right in the order added.  An optional map can be supplied to augment or override the attributes in the generated <input type="button" ... /> fragment.
*/
WizardAlert.prototype.AddButton = function(inButtonLabel, inReturnCode, inOptAttributes)
{
	this.buttons.push({ label: inButtonLabel, returnCode: inReturnCode, attributes: inOptAttributes });
}


/**
A class to generate standard alerts used in a couple different places.
*/
function StandardAlert(inContainerProxy, inLocalizationObj)
{
	this.containerProxy = inContainerProxy;
	this.localizationObj = inLocalizationObj;
}


/**
Utility string lookup method to simplify handling the case where we don't have a Localization object
handy.
*/
StandardAlert.prototype._LoadText = function(inStringID, inDefaultText, inPropertyMap)
{
	var locText = inDefaultText;
	if (null == inPropertyMap)
	{
		inPropertyMap = this.containerProxy.GetDefaultProperties();	
	}
	if (null != this.localizationObj)
	{
		locText = this.localizationObj.GetString(inStringID, inDefaultText, inPropertyMap);
	}
	return locText;
	
}

/**
Standard invalid user credentials alert.
*/
StandardAlert.prototype.InvalidUserCredentials = function()
{
	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(this._LoadText("locInvalidUserCredentialsTitle", "Invalid User Credentials"));
	a.SetBody("<p>" + this._LoadText("locInvalidUserCredentialsBody", "You do not have sufficient security credentials to install this software.") + "<p>");
	a.AddButton(this._LoadText("locBtnQuit", "Quit"), "2");
	return a.Run();
	*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	var extraOptions = {strAlertIcon:"error"};
	new WizardAlert1(this.containerProxy, this._LoadText("locInvalidUserCredentialsTitle", "Invalid User Credentials"), "<p>" + this._LoadText("locInvalidUserCredentialsBody", "You do not have sufficient security credentials to install this software.") + "</p>", buttonsArray, "",extraOptions);
}


/**
Standard Setup already running alert.
*/
StandardAlert.prototype.SetupAlreadyRunning = function()
{
	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(this._LoadText("locSetupRunningTitle", "Setup Already Running"));
	a.SetBody("<p>" + this._LoadText("locSetupRunningBody", "You can only install one Adobe product at a time.  Please complete the other installation before attempting to install this product." + "</p>"));
	a.AddButton(this._LoadText("locQuit", "Quit"), "2");
	return a.Run();*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	var extraOptions = {strAlertIcon:"warning"};
	new WizardAlert1(this.containerProxy, this._LoadText("locSetupRunningTitle", "Setup Already Running"), "<p>" + this._LoadText("locSetupRunningBody", "You can only install one Adobe product at a time.  Please complete the other installation before attempting to install this product." + "</p>"), buttonsArray, "",extraOptions);
}

/**
Standard Generic alert.
*/
StandardAlert.prototype.GenericError = function()
{
	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(this._LoadText("locSetupRunningTitle", "Setup Already Running"));
	a.SetBody("<p>" + this._LoadText("locSetupRunningBody", "You can only install one Adobe product at a time.  Please complete the other installation before attempting to install this product." + "</p>"));
	a.AddButton(this._LoadText("locBtnQuit", "Quit"), "2");
	return a.Run();*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	var extraOptions = {strAlertIcon:"error"};
	new WizardAlert1(this.containerProxy, this._LoadText("locGenericError", "Main Setup rolled back unexpectedly!"), "<p>" + this._LoadText("locnotGenericErrorBody", "Update Failed Please contact adobe.com" + "</p>"), buttonsArray, "",extraOptions);
}
/**
Standard Higher Patch installed alert.
*/
StandardAlert.prototype.HigherPatchInstalled = function()
{
	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(this._LoadText("locSetupRunningTitle", "Setup Already Running"));
	a.SetBody("<p>" + this._LoadText("locSetupRunningBody", "You can only install one Adobe product at a time.  Please complete the other installation before attempting to install this product." + "</p>"));
	a.AddButton(this._LoadText("locBtnQuit", "Quit"), "2");
	return a.Run();*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	new WizardAlert1(this.containerProxy, this._LoadText("locHigherPatchError", "Newer version detected"), "<p>" + this._LoadText("locHigherPatchErrorBody", "[product] is already updated to a newer version." + "</p>"), buttonsArray, "");
}
/**
Standard Already installed alert.
*/
StandardAlert.prototype.AlreadyInstalled = function()
{
	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(this._LoadText("locSetupRunningTitle", "Setup Already Running"));
	a.SetBody("<p>" + this._LoadText("locSetupRunningBody", "You can only install one Adobe product at a time.  Please complete the other installation before attempting to install this product." + "</p>"));
	a.AddButton(this._LoadText("locBtnQuit", "Quit"), "2");
	return a.Run();*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	new WizardAlert1(this.containerProxy, this._LoadText("locReinstallError", "Update already installed"), "<p>" + this._LoadText("locReinstallErrorBody", "The update is already installed on your machine!" + "</p>"), buttonsArray, "");
}
/**
Standard Base payload not installed alert.
*/
StandardAlert.prototype.BasePayloadNotInstalled = function()
{
	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(this._LoadText("locSetupRunningTitle", "Setup Already Running"));
	a.SetBody("<p>" + this._LoadText("locSetupRunningBody", "You can only install one Adobe product at a time.  Please complete the other installation before attempting to install this product." + "</p>"));
	a.AddButton(this._LoadText("locBtnQuit", "Quit"), "2");
	return a.Run();*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	new WizardAlert1(this.containerProxy, this._LoadText("locBasePayloadError", "[product] not installed"), "<p>" + this._LoadText("locBasePayloadErrorBody", "Unable to detect [product]. [product] is required to be installed to apply this update." + "</p>"), buttonsArray, "");
}
/**
Standard Setup Windows update running alert.
*/
StandardAlert.prototype.WindowsUpdateRunning = function()
{
	var button1 = {label:this._LoadText("locBootStrapQuit", "Quit"), left:"187px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var button2 = {label:this._LoadText("locBootStrapContinue", "Continue"), left:"325px", top:"106px", returnCode:"2", hotkey:"C", defaultOption:"0"};
	var buttonsArray = new Array(button1, button2);
	var WA1 = new WizardAlert1(this.containerProxy, this._LoadText("locAlertWindowTitle", "Installer Alert"),
		"<p>"
		+ "<span>"
		+ this._LoadText("systemPageWindowsUpdatesRunning1",
			"Setup has detected that Windows Updates are being installed. It is recommended that you continue installing the updates, reboot and then run this setup again.")
		+ "</span><br/><span>"
		+ this._LoadText("systemPageWindowsUpdatesRunning2",
			"Running this setup while Windows Updates are being installed may result in an invalid installation.")
		+ "</span><br/><span>"
		+ this._LoadText("systemPageWindowsUpdatesRunning3",
			"Click Quit to exit the installer or click continue to proceed with the installation.")
		+"</span></p>",
		buttonsArray, this._LoadText("locWindowUpdateRunningTitle", "Windows Updates Running"));
	return WA1;
}
/**
Standard Conflicting Processes Running alert.
*/
StandardAlert.prototype.ConflictingProcessesRunning = function(inProcessList)
{
	var processes = inProcessList.split("<br>");
	var multi = processes.length;
	
	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"187px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var button2 = {label:this._LoadText("locRetry", "Retry"), left:"325px", top:"106px", returnCode:"2", hotkey:"R", defaultOption:"0"};
	var buttonsArray = new Array(button1, button2);
	var extraOptions = {bDoubleHeight:"1", strAlertIcon:"warning"};
	var wa;
	if(multi > 2)
	{
		wa = new WizardAlert1(this.containerProxy, this._LoadText("locConflictingProcessError", "Applications Running"), "<p>" + this._LoadText("locConflictingProcessErrorBody", "Please close the following applications and retry to continue installing updates:") + "<br>" + inProcessList +  "</p>", buttonsArray, "",extraOptions);
	}
	else
	{
		wa = new WizardAlert1(this.containerProxy, this._LoadText("locConflictingProcessErrorSing", "Application Running"), "<p>" + this._LoadText("locConflictingProcessErrorBodySing", "Please close the following application and retry to continue installing update:") + "<br>" + inProcessList +  "</p>", buttonsArray, "",extraOptions);
	}
		
	var bQuit = false;

	if (wa.returnValue == "1") {
		gSession.UIExitDialog(wa.returnValue);
		bQuit = true;
	}
//	else        
//	    CheckForConflictingProcess();

	return bQuit;
}

/**
Standard Not Enough Disk Space alert.
*/
StandardAlert.prototype.NotEnoughDiskSpace = function(prop)
{

	var button1 = {label:this._LoadText("locQuit", "Quit"), left:"187px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var button2 = {label:this._LoadText("locRetry", "Retry"), left:"325px", top:"106px", returnCode:"2", hotkey:"R", defaultOption:"0"};
	var buttonsArray = new Array(button1, button2);
	var extraOptions = {strAlertIcon:"warning"};
	var wa;
	wa = new WizardAlert1(this.containerProxy, this._LoadText("locnotEnoughDiskSpace", "Not Enough Disk Space"), "<p>" + this._LoadText("locnotEnoughDiskSpaceBody", "Please free at least [spaceReq] MB of diskspace on volume [volume] and try again." + "</p>",prop), buttonsArray, "",extraOptions);
	
	var bQuit = false;
	if (wa.returnValue == "1") {
		gSession.UIExitDialog(wa.returnValue);
		bQuit = true;
	}
	else        
	    CheckForDiskSpace();
	
	return bQuit;

}

/**
Standard bootstrap failure alert.
*/
StandardAlert.prototype.BootstrapInstallFailure = function(inExitCode, inErrorArgs)
{
	var propMap = this.containerProxy.GetDefaultProperties();
	var errorText = "";
	if (inErrorArgs["MSIErrorText"])
		errorText += inErrorArgs["MSIErrorText"];
	if (inErrorArgs["ARKErrorText"])
		errorText += inErrorArgs["ARKErrorText"];
	
	var locTitle = this._LoadText("locBootstrapFailedTitle", "Setup Failed");
	var locBody = "<p>" + this._LoadText("locBootstrapFailedBody", "Setup failed to bootstrap.") + "</p>";
	if (errorText && errorText.length > 0)
	{
		var errorTextNode = document.createElement("p");
		errorTextNode.id = "alertErrorText";
		var errorText = document.createTextNode(errorText);
		errorTextNode.appendChild(errorText);
		locBody += errorTextNode.innerHTML;
	}

	/*var a = new WizardAlert(this.containerProxy);
	a.SetTitle(locTitle);
	a.SetBody(locBody);
	a.AddButton(this._LoadText("locBtnQuit", "Quit"), "2");
	return a.Run();
	*/
	//WizardAlert1(inSession, inTitle, inBody, buttonsArray, subTitle)
	var button1 = {label:this._LoadText("locBootStrapQuit", "Quit"), left:"256px", top:"106px", returnCode:"1", hotkey:"Q", defaultOption:"7"};
	var buttonsArray = new Array(button1);
	var _WA1=new WizardAlert1(this.containerProxy, locTitle, locBody, buttonsArray, "");
	return _WA1;
}

// END WizardAlert
///////////////////////////////////////////////////////////////////////


function FormatLogHeader(inHeaderText)
{
    return "______ " + inHeaderText + " ______"
}

/**
Property turn JSON string into JS object by encapsulating in parens.
@param	inJSONText	JSON text
@returns	Re-hydrated object, null if JSON invalid.
*/
function _jsonToObject(inJSONText)
{
	var retObj = null;
	try
	{
		retObj = eval('(' + inJSONText + ')');
	}
	catch (e)
	{
		retObj = null;
	}
	return retObj;	
}

/**
Concatenate two strings given the current platform string
@param	inPathComp		Array of paths to concatenate
@param	inPlatName		Platform name
@returns Concat'd paths
*/
function _concatPaths(inPathComp, inPlatName)
{
	
	var retString = "";
	var pathComp = "";
	var pathSep = '/';
	
	// If the first path component starts with a UNC marker then
	// we need to preserve this after normalization
	// 1442989
	var uncPath = false;

	if (inPathComp[0]) 
	{
		if (inPathComp[0].length > 1)
		{
			uncPath = (inPathComp[0].charAt(0) == '\\' && inPathComp[0].charAt(1) == '\\');
		}
		
		for (var i = 0; i < inPathComp.length; i++)
		{
			retString += (inPathComp[i] + "/");
		}
		
		// Convert all backslashes to forward slashes
		retString = retString.replace(/\\+/g, "/");
		if ("Win32" == inPlatName)
		{
			retString = retString.replace(/\/+/g, "\\"); 
			if (uncPath)
			{
				retString = '\\' + retString;
			}
		}
		else
		{
			retString = retString.replace(/[\/\:]+/g, "/"); 
		}
		if (retString.charAt(retString.length-1) == '\\' ||
			retString.charAt(retString.length-1) == '/')
		{
			retString = retString.slice(0, retString.length-1);
		}
	} 
	else 
	{
		throw "Invalid paths provided to _concatPaths";
	}

	return retString;
}

/**
Returns an array of DOM elements under a parent with a specified classname
@param	oElm		Parent element
@param	strTagName		Tag name - span, div, *, ...
@param	strClassName		Class name
*/

function getElementsByClassName(oElm, strTagName, strClassName)
{
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = new Array();
	strClassName = strClassName.replace(/\-/g, "\\-");
	var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
	var oElement;
	for(var i=0; i<arrElements.length; i++){
		oElement = arrElements[i];
		if(oRegExp.test(oElement.className)){
			arrReturnElements.push(oElement);
		}
	}
	return (arrReturnElements)
}
/**
Apply inFunction to inNode as well as all of inNode's children
@param	inNode 	DOM element
@param	inFunction Unary function taking DOMNode to apply to all elements
*/
function applyToDOMFragment(inNode, inFunction)
{
	inFunction(inNode);
	
	if (inNode.all)
	{
		for (var i = 0; i < inNode.all.length; ++i)
			inFunction(inNode.all[i]);
	}
	else if (null != inNode.getElementsByTagName)
	{
		var childNodes = inNode.getElementsByTagName('*');
		for (var i = 0; i < childNodes.length; ++i)
			inFunction(childNodes[i]);
	}
	else
	{
		var childNodes;
		
		if (inNode.childNodes != null)
			childNodes = inNode.childNodes;
		else if (inNode.children != null)
			childNodes = inNode.children;
		
		if (childNodes != null)
		{
			// make a copy of the node list so that manipulations of
			// the list will not affect the traversal
			var childNodesArray = new Array;
			for (var nodeIndex = 0; nodeIndex < childNodes.length; ++nodeIndex)
				childNodesArray.push(childNodes[nodeIndex]);
			
			// iterate node list copy
			for (var i = 0; i < childNodesArray.length; ++i)
				applyToDOMFragment(childNodesArray[i], inFunction);
		}
	}
}

//! Deletes all the descendants of the elementName.
//! \returns, the node of the with the Id elementName.
function RemoveAllChildren(element)
{
	if (element)
	{
		while (element && element.childNodes[0]) {
			element.removeChild(element.childNodes[0]);
		}
	}
	return element;
}
