/**
 * -----------------------------------------------------------------
 * Copyright (c) 2004-2008 Fluid, Inc. All Right Reserved.
 * This software is the proprietary information of Fluid, Inc.
 * Use is subject to strict licensing terms.
 * -----------------------------------------------------------------
 *
 * concept_lib.js
 *
 * A Javascript library for the embed Fluid Retail (TM) interactive
 * displays.
 *
 */

if ( this.Concept == null )	Concept = {}
			
if ( Concept.registry == null )
{
	Concept.registry = {}
	Concept.registry.ind = 1;
	Concept.registry.divInfo = {};
	Concept.registry.lastDisplayInfo = {};
}
 
 /**
  * Embed a product viewer. This function will dynamically generate an OBJECT/EMBED
  * tag for the Flash player, unless Flash detection fails
  * 
  * @param params An object with the following properties:
  * REQUIRED:
  * 	width                     The width of the product viewer.
  * 	height                    The height of the product viewer.
  * 	baseURL                   The base url of the conceptretail files.
  * 	customerId                The identifier of the customer.
  * 	productId                 The identifier of the given product to embed.
  * 	displayId                 The identifier of the display for this product (e.g. "detail").
  * OPTIONAL: 
  * 	bgColor                   The background color of the viewer (e.g. #FFFFFF).
  * 	preloaderBgColor          The background color of the preloader (e.g. #FFCC00).
  * 	preloaderFgColor          The foreground color of the preloader (e.g. #FF0000).
  * 	errorBGColor              The background color used for error messages
  * 	errorFontFace             The font used for error messages
  * 	errorFontColor            The font color used for error messages
  * 	errorFontSize             The font size used for error messages
  * 	detailErrorFontFace       The font used for detalied error messages
  * 	detailErrorFontColor      The font color used for detalied error messages
  * 	detailErrorFontSize       The font size used for detalied error messages
  * 	preloaderMessage          The message to display below the preloader.
  * 	logFunction               The name of the Javascript function to invoke for logging. No parameter indicates no logging.
  * 	cacheControlId            An identifier that gets appended to pview / pviewer XML files to assist with cache issues (e.g. fall2004).
  * 	wmode                     The transparency mode of the player. Valid values: "window", "transparent, or "opaque" (default).
  * 	displayControlXMLPath 		The complete URL to the displayControl XML file. 
  * 	displayControlXML         A String representation of an XML config file (e.g "<display-control id=\"123\"> .... </display-control>").
  * 	noFlashImageURL           A URL to a fallback image to be used if the user doesn't have Flash.
  * 	onLoadHandler             The name of a JavaScript function that will handle an on load callback.
  * 	onErrorHandler            The name of a JavaScript function that will handle errors
  * 	extraVariables            A list a extra variables in query string format that can be used to customize text on the Display.
  * 
  * @returns The DOM Element ID to the ProductViewer Flash for use with Javascript communication.
  */
function embedProductViewer( params ) 
{
	var width = params.width;
	var height = params.height;
	
	var baseURL = params.baseURL;
	var productId = params.productId;
	var displayId = params.displayId;
	var customerId = params.customerId;
	var wmode = params.wmode || "opaque";
	var elementId;
	
	if ( params.baseURL == null ) params.baseURL = "";
	if ( params.baseURL != "" && params.baseURL.charAt( params.baseURL.length - 1 ) != "/" ) params.baseURL += "/";
	
	if ( hasFlash() )
	{
		// define our namespace
			
		Concept.registry.nextLCID = "CR_LCID_" + Concept.registry.ind++ + "_" + (new Date()).getTime();
		
		
		var preloaderURL = params.baseURL + "standard/v2/swf/cengage_preloader.swf";
		var productViewURL = "../../../customers/" + customerId + "/" + productId + "/" + productId + "_" + displayId + 
							 "/pview_" + productId + "_" + displayId + ".xml";
		
	 	var bgColor = params.bgColor || "#FFFFFF";
    var preloaderBgColor = params.preloaderBgColor || "#666666";
	 	var preloaderFgColor = params.preloaderFgColor || "#CCCCCC";
	
		// change it to appropriate flash format
		preloaderBgColor = "0x" + preloaderBgColor.substring(1);
		preloaderFgColor = "0x" + preloaderFgColor.substring(1);

		var flashVars = 'productViewXML=' + productViewURL; 
		flashVars += '&productId=' + productId;
		flashVars += '&bgColor=' + bgColor;
		flashVars += '&preloaderBGColor=' + preloaderBgColor;
		flashVars += '&preloaderFGColor=' + preloaderFgColor;
		
		if(params.errorBGColor != null) flashVars += '&errorBGColor=' + params.errorBGColor;
		if(params.errorFontFace != null) flashVars += '&errorFontFace=' + params.errorFontFace;
		if(params.errorFontColor != null) flashVars += '&errorFontColor=' + params.errorFontColor;
		if(params.errorFontSize != null) flashVars += '&errorFontSize=' + params.errorFontSize;
		if(params.detailErrorFontFace != null) flashVars += '&detailErrorFontFace=' + params.detailErrorFontFace;
		if(params.detailErrorFontColor != null) flashVars += '&detailErrorFontColor=' + params.detailErrorFontColor;
		if(params.detailErrorFontSize != null) flashVars += '&detailErrorFontSize=' + params.detailErrorFontSize;
		
		flashVars += '&width=' + width;
		flashVars += '&height=' + height;
		flashVars += '&zoomWinLCID=' + Concept.registry.nextLCID;
		flashVars += '&htmlURL=' + window.location.host;
		flashVars += '&preloaderMessage=' + params.preloaderMessage;
		
		if(params.logFunction != null) flashVars +='&logFunction=' + params.logFunction;
		if(params.cacheControlId != null) flashVars += '&cacheControlId=' + params.cacheControlId;
		if(params.displayControlXMLPath != null) flashVars += '&displayControlXMLPath=' + params.displayControlXMLPath;
		if(params.displayControlXML != null) flashVars += '&displayControlInfo=' + encodeURIComponent(params.displayControlXML);
		if(params.extraVariables != null) flashVars += '&extraVariables=' + encodeURIComponent(params.extraVariables);
		if(params.onLoadHandler != null) flashVars += "&onLoadHandler=" + params.onLoadHandler;
		if(params.onErrorHandler != null) flashVars += "&onErrorHandler=" + params.onErrorHandler;
		
		elementId = "cengage_preloader_" + Concept.registry.ind;
		
		// IE VBScript to handle fscommands
		var scriptToWrite = '';
		scriptToWrite += '<script language="VBScript">';
		scriptToWrite += 'Sub ' + elementId + '_FSCommand(ByVal command, ByVal args)';
		scriptToWrite += 'call ' + elementId + '_DoFSCommand(command, args)';
		scriptToWrite += 'end sub';
		scriptToWrite += '</script>';
		document.writeln(scriptToWrite);
	
		// JavaScript method to handle fscommands
		this[elementId + "_DoFSCommand"] = function(command, args)
		{
			// Check for FSCommand: in the command/function name as is the case on the Mac
			if(command.indexOf("FSCommand:") == 0) command = command.split("FSCommand:")[1];
			var func = this[command];
			if(func != null) func(args);
		}
		
		var htmlFragment = "";
		htmlFragment += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="' + width 
						+ '" height="' + height + 
						'" id="' + elementId + '" align="top">';
		htmlFragment += '<param name="movie" value="' + preloaderURL + '" />';
		htmlFragment += '<param name="allowScriptAccess" value="always" />';
		htmlFragment += '<param name="bgcolor" value="' + bgColor + '" />';
		htmlFragment += '<param name="base" value="." />';
		htmlFragment += '<param name="wmode" value="' + wmode + '" />';
 		htmlFragment += '<param name="flashvars" value="' + flashVars + '" />';
		htmlFragment += '<embed swliveconnect="true" src="' + preloaderURL + '" base="." quality="high" bgcolor="' + bgColor + 
						'" width="' + width +'" height="' + height + '"' + '"wmode="' + wmode + '"' +
						' name="' + elementId + '" align="top" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"';
		htmlFragment +=	' flashvars="' + flashVars + '" >';
		htmlFragment += '</embed></object>';
	 	//alert(htmlFragment);
		document.writeln(htmlFragment);
		
		// track the info for the last display embed. needed for personalization
		var info = {};
		info.embedElementId = elementId;
		info.customerId = params.customerId;
		info.productId = params.productId;
		info.displayId = params.displayId;
		info.baseURL = params.baseURL;
		
		Concept.registry.lastDisplayInfo = info;

	}
	else
	{
		var noFlashImageURL = params.noFlashImageURL;
		if (noFlashImageURL == null)
		{
			var displayControlIds;

			if(params.displayControlXMLPath != null)
			{
				var response = executeSyncXmlRequest(params.displayControlXMLPath);
				if(response.success)
				{
					displayControlIds = getInventoryIdsForFallback(response.xml);
				}
			}
			else if (params.displayControlXML != null)
			{
				displayControlIds = getInventoryIdsForFallback(getXmlFromString(params.displayControlXML));
			}
			
			var variationInvId;
			var viewInvId;
			
			if(displayControlIds != null)
			{
				variationInvId = displayControlIds.variationInvId;
				viewInvId = displayControlIds.viewInvId;
			}
			
			var fallbackHTML = getFallbackHtml(params.baseURL, customerId, productId, displayId, variationInvId, viewInvId);
		}
		else
		{
			var fallbackHTML = '<img src="' + noFlashImageURL +'" />';
		}

		document.writeln(fallbackHTML);
	}
	
	return elementId;
}

/**
 * Embed an external zoom window
 * 
 * @param params An associative array of the following parameters
 * REQUIRED:
 *     - baseURL          The baseURL of the conceptretail files.
 *	   - zoomWinX         The absolute x-pos of the external zoom window
 *	   - zoomWinY         The absolute y-pos of the external zoom window
 *	   - zoomWinWidth     The width of the external zoom window
 *	   - zoomWinHeight    The height of the external zoom window
 * OPTIONAL: 
 *     - bgColor          The background color of the viewer (default #FFFFFF).
 *	   - borderColor      The color of the border around the viewer (default #CCCCCC).
 *	   - borderWidth      The width of the border around the viewer (default 1).
 *	   - zIndex           The z-index of the viewer (default 99).
 */
function embedExternalWindowDiv( params )
{
	if ( hasFlash() )
	{
		if ( params.baseURL == null )
			params.baseURL = "";
		else
		{
			if ( params.baseURL != "" && params.baseURL.charAt( params.baseURL.length - 1 ) != "/" )
			params.baseURL += "/";
		}
		if ( params.bgColor == null )
			params.bgColor = "#FFFFFF";
		if ( params.borderColor == null )
			params.borderColor = "#CCCCCC";
		if ( params.borderWidth == null )
			params.borderWidth = 1;
		if ( params.zIndex == null )
			params.zIndex = 99;
	
		var externalWinURL = params.externalWinURL;
		var zoomWinX = params.zoomWinX;
		var zoomWinY = params.zoomWinY;
		var zoomWinWidth = params.zoomWinWidth;
		var zoomWinHeight = params.zoomWinHeight;
		var externalWinURL = params.baseURL + "standard/v2/swf/cengage_external_win.swf";  
		
		var zoomWinLCID = (params.zoomWinID != null) ? params.zoomWinID : Concept.registry.nextLCID;
		
		var flashVars = "zoomWinLCID=" + zoomWinLCID;
 		flashVars += "&width=" + zoomWinWidth;
		flashVars += "&height=" + zoomWinHeight;
		flashVars += "&htmlURL=" + window.location.host;
		if(params.scrollerBaseURL != null) flashVars += "&offsetBaseURL=" + params.scrollerBaseURL;
		
		// store the position in a registry; we start with the div's offscreen
		Concept.registry.divInfo[zoomWinLCID] =  { x:zoomWinX, y:zoomWinY };
		
		var elementId = "cengage_external_win_" + ((params.zoomWinID != null) ? params.zoomWinID : Concept.registry.ind);
		
		// IE VBScript to handle fscommands
		var scriptToWrite = '';
		scriptToWrite += '<script ' + 'language="VBScript">';
		scriptToWrite += 'Sub ' + elementId + '_FSCommand(ByVal command, ByVal args)';
		scriptToWrite += 'call ' + elementId + '_DoFSCommand(command, args)';
		scriptToWrite += 'end sub';
		scriptToWrite += '</scr' + 'ipt>';
		document.writeln(scriptToWrite);
	
		// JavaScript method to handle fscommands
		this[elementId + "_DoFSCommand"] = function(command, args)
		{
			// Check for FSCommand: in the command/function name as is the case on the Mac
			if(command.indexOf("FSCommand:") == 0) command = command.split("FSCommand:")[1];
			var func = this[command];
			if(func != null) func(args);
		}
		
		var htmlFragment = "";
		htmlFragment += '<div id="' + zoomWinLCID + '" style="padding:0px;position:absolute; left:-10000px; top:-10000px; width:' + (zoomWinWidth+2) + 'px; height:' + (zoomWinHeight+2) + 'px; z-index:' + params.zIndex + ';">';
		
		if(document.fireEvent && window.external && (typeof window.XMLHttpRequest == "undefined")){//dropdown hack IE6
		htmlFragment += '<iframe style="position:absolute;top:0;left:0;border:none;height:100%;width:100%;filter:alpha(opacity=0);" src="javascript:;"></iframe>';
		//alert("fix");
		};
		
		htmlFragment += '<div style="position:absolute;border-style:solid;border-width:' + params.borderWidth + 'px;border-color:' + params.borderColor + ';">';
		htmlFragment += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="' + 
						zoomWinWidth + '" height="' + zoomWinHeight + '" id="' + elementId + '" align="top" swLiveConnect="true">';
		htmlFragment += '<param name="movie" value="' + externalWinURL + '" />';
		htmlFragment += '<param name="allowScriptAccess" value="always" />';
		htmlFragment += '<param name="bgcolor" value="' + params.bgColor + '" />';
		htmlFragment += '<param name="scale" value="noscale" />';
		htmlFragment += '<param name="base" value="." />';
		htmlFragment += '<param name="flashvars" value="' + flashVars + '" />';
		htmlFragment += '<embed src="' + externalWinURL + '" base="." quality="high" scale="noscale" bgcolor="' + params.bgColor + '" width="' + zoomWinWidth + '" height="' + zoomWinHeight + '" name="' + elementId + '" align="top" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"';
		htmlFragment +=	' flashvars="' + flashVars + '" >';
		htmlFragment += '</embed></object></div></div>';
		//alert(htmlFragment);
		document.writeln(htmlFragment);
		
		return elementId;
	}
}

/**
 * Embed an HTML personalization UI into the given DIV. If no XML data is found, 
 * the personalization UI will not be displayed. 
 * @param	divId The identifier of the DIV that we will render the personalization UI into.
 * @param	displayInfo - An optional object that describes a display. The object contains
 * the following items.
 * 	- baseURL
 * 	- customerId
 * 	- productId
 * 	- displayId
 * 	- embedElementId - The id of the SWF containing the personalization fields
 * If the displayInfo is not defined the method uses Concept.registry.lastDisplayInfo to 
 * retrieve the same information
 *
 * NOTE: This will automatically wire the UI to the *last* embedded interactive display. 
 */
function embedPersonalizationUI( divId, displayInfo )
{
	// generate the personalization XML string
	var info = (displayInfo != null) ? displayInfo : Concept.registry.lastDisplayInfo;
	
	var pXML = info.baseURL + "customers/" + info.customerId + "/" + info.productId + "/" + info.productId + "_";
	pXML += info.displayId + "/personalization_" + info.productId + "_" + info.displayId + ".xml";
	
	// create a callback holding a reference to the last Concept preloader Flash embed.
	var callback = new Object();
	
	callback.handler = this;
	callback.embedElementId = info.embedElementId;
	callback.divId = divId;
	callback.onChanged = function( textId )
	{
		this.handler.onPersonalizationChanged( textId, this.embedElementId, this.divId );
	}

	
	Concept.Personalization.generateUI( divId, pXML, callback, callback.onChanged );
}

/**
 * Callback from personalization UI that the personalization information changed.
 * @param textId The identifier of the personalization text that changed. 
 */
function onPersonalizationChanged( textId, embedElementId, divId )
{
	var formState = Concept.Personalization.getFormState( divId );
	var textState;
	for ( var i=0;i<formState.length;i++ )
	{
		textState = formState[i];
		if ( textState.id == textId )
		{
			var val = textState.id + ":::" + textState.text + ":::" + textState.fontId + ":::" + textState.color;
			var embedElement = getFlashMovieObject( embedElementId );
			//alert( "embed: " + embedElement + " : " + embedElementId );
			embedElement.SetVariable( "personalizedTextChanged", val );
		}
	}
}

/**
 * Utility function to select or unselect a Category within Concept.
 * @param categoryGroupId The ID of the CategoryGroup ( VIEW, VARIATION, HOTSPOT, IMAGETYPE )
 * @param inventoryId The inventoryId of the Category.
 */
function selectCategory( categoryGroupId, inventoryId )
{
	executeConceptInternalCommand( buildCommand( categoryGroupId, inventoryId ) );
}

/**
 * Utility function to select or unselect a Category within Concept.
 * @param embedId The id of the embedded swf element
 * @param categoryGroupId The ID of the CategoryGroup ( VIEW, VARIATION, HOTSPOT, IMAGETYPE )
 * @param inventoryId The inventoryId of the Category.
 */
function selectCategoryById( embedId, categoryGroupId, inventoryId )
{
	executeConceptInternalCommand( buildCommand( categoryGroupId, inventoryId ) , embedId );
}

function buildCommand( categoryGroupId, inventoryId )
{
	var command = "";
	if ( inventoryId == null )
	{
		command = "concept://unselect:" + categoryGroupId;
		
		// For hotspots, when we unselect we need to select a MAIN image
		if ( categoryGroupId.toUpperCase() == "HOTSPOT" )
		{
			command += ";select:category:IMAGETYPE:MAIN";
		}
			
	}
	else
	{
		command = "concept://select:category:" + categoryGroupId + ":" + inventoryId;
		if ( categoryGroupId.toUpperCase() == "HOTSPOT" )
		{
			// For hotspots, when we select one we need to make sure to select a ZOOM image.
			command += ";select:category:IMAGETYPE:ZOOM"
		}
	}
	
	return command;
}

/**
 * Execute any command that can be understood by the InternalLinkVariableEvaluator class. The 
 * following is likely the most useful to know in the Javascript context:
 *
 * - concept://select:category:[cg]:[cat]   Select the Category in the [cg] CategoryGroup with the [cat] Category, 
 * 											where [cat] is a Category inventoryId or ID.  [cg is VIEW, VARIATION, HOTSPOT, or IMAGE_TYPE]
 * - concept://unselect:[cg]			    Unselect the selected category in the [cg] CategoryGroup.
 *
 * @param command	a command to execute
 * @param pEmbedId	optional embedId for the swf element
 */
function executeConceptInternalCommand( command, pEmbedId )
{
	//alert( "command: " + command);
	var embedId = ( pEmbedId ) ? pEmbedId : Concept.registry.lastDisplayInfo.embedElementId;
	var embedElement = getFlashMovieObject( /*Concept.registry.lastDisplayInfo.embedElementId*/embedId );
	embedElement.SetVariable( "conceptInternalCommand", command ); 
}

/** invoked on a click in the focused window - bug fix **/
function concept_focusAway() {
        cINPUT = document.getElementsByTagName('INPUT');

        for ( var i=0 ; i<cINPUT.length ; i++ )
        {
                if ( cINPUT[i].textId )
                {
                        cINPUT[i].blur();
                }
        }
}

function showZoomWindow( id )
{
	var div = getDiv( id );
	var pos = Concept.registry.divInfo[ id ];
	div.style.left = pos.x + "px";
	div.style.top = pos.y + "px";
	toggleDiv(id, 1);
}

function hideZoomWindow( id )
{
	toggleDiv(id, 0);
}

function toggleDiv(szDivID, iState) // 1 visible, 0 hidden
{
    if(document.layers)	   //NN4+
    {
       document.layers[szDivID].visibility = iState ? "show" : "hide";
    }
    else if(document.getElementById)	  //gecko(NN6) + IE 5+
    {
        var obj = document.getElementById(szDivID);
        obj.style.visibility = iState ? "visible" : "hidden";
        
        if ( ! iState )
        {
          // WORKAROUND for Firefox on OSX. forces rerendering
          obj.style.padding = '10px';
          obj.style.padding = '0px';
        }
        
    }
    else if(document.all)	// IE 4
    {
        document.all[szDivID].style.visibility = iState ? "visible" : "hidden";
    }
}

function getDiv(szDivID)
{
	
    if(document.layers)    //NN4+
    {
       return document.layers[szDivID];
    }
    else if(document.getElementById)      //gecko(NN6) + IE 5+
    {
        return document.getElementById(szDivID);
    }
    else if(document.all)       // IE 4
    {
        return document.all[szDivID];
    }
}

function getFlashMovieObject(movieName)
{
  if (window.document[movieName]) 
  {
      return window.document[movieName];
  }
  if (navigator.appName.indexOf("Microsoft Internet")==-1)
  {
    if (document.embeds && document.embeds[movieName])
      return document.embeds[movieName]; 
  }
  else // if (navigator.appName.indexOf("Microsoft Internet")!=-1)
  {
    return document.getElementById(movieName);
  }
}

function hasFlash()
{
	var plugin = (navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]) ? 	navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin : 0;
	
	if ( plugin )
	{
		var MM_contentVersion = 8;
		var words = navigator.plugins["Shockwave Flash"].description.split(" ");
		for (var i = 0; i < words.length; ++i)
		{
			if (isNaN(parseInt(words[i])))
				continue;
			var MM_PluginVersion = words[i]; 
	    }
		this.MM_FlashCanPlay = MM_PluginVersion >= MM_contentVersion;
	}
	else if (navigator.userAgent && navigator.userAgent.indexOf("MSIE")>=0 
	   && (navigator.appVersion.indexOf("Win") != -1))
	{
		document.write('<SCR' + 'IPT LANGUAGE=VBScript\> \n'); //FS hide this from IE4.5 Mac by splitting the tag
		document.write('on error resume next \n');
		document.write('MM_FlashCanPlay = ( IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.7")))\n');
		document.write('</SCR' + 'IPT\> \n');
	}	
	return this.MM_FlashCanPlay; 
}

//

/**
 * Parses Display Control XML data and returns the found
 * view and variation IDs used for the fall back image 
 * @return Object
 *				- variationId
 * 				- viewId
 */
function getInventoryIdsForFallback(displayControlXml)
{
	// <display-control id='123'>
	// <views active='1,2,3' order='sequential' default='2' />
	// <variations active='1,2,3' order='sequential' />
	// </display-control>
	
	var ids = new Object();
	
	var variationNode = displayControlXml.getElementsByTagName("variations")[0];
	if(variationNode != null)
	{
		// Careful, default is a keyword, hence this syntax (fails in IE otherwise).
		var defaultAttr = variationNode.attributes["default"];
		if(defaultAttr != null)
		{
			ids.variationInvId = defaultAttr.value;
		}
		else
		{
			var activeAttr = variationNode.attributes.active;
			if(activeAttr != null) ids.variationInvId = activeAttr.value.split(",")[0];
		}
	}
	
	var viewNode = displayControlXml.getElementsByTagName("views")[0];
	if(viewNode != null)
	{
		// Careful, default is a keyword, hence this syntax (fails in IE otherwise).
		var defaultAttr = viewNode.attributes["default"];
		if(defaultAttr != null)
		{
			ids.viewInvId = defaultAttr.value;
		}
		else
		{
			var activeAttr = viewNode.attributes.active;
			if(activeAttr != null) ids.viewInvId = activeAttr.value.split(",")[0];
		}
	}

	return ids;
}

/**
 * Fetches product xml data and determines what the fall back HTML should be.
 * The fall back HTML is used when the Flash Player can not be found and typically 
 * consists of the first main image found in the Display.
 * @param customerId - The ID of the Customer
 * @param productId - The ID of the Product
 * @param displayId - The ID of the Display
 * @param variationInvId - The variation inventory Id of the image to use. Only provided when displayControlXML is used.
 * @param viewInvId - The view inventory Id of the image to use. Only provided when displayControlXML is used.
 * @return String - The URL to the image
 */
function getFallbackHtml(baseUrl, customerId, productId, displayId, variationInvId, viewInvId)
{
	var fallbackHtml = null; // baseURL + "customers/" + customerId + "/" + productId + "/" + productId + "_" + displayId + "/fallback.jpg";
	var displayXmlUrl = baseURL + "customers/" + customerId + "/" + productId + "/" + productId + "_" + displayId + "/pview_" + productId + "_" + displayId + ".xml";
	
	var response = executeSyncXmlRequest(displayXmlUrl);
	if(response.success)
	{
		var imageUrl = getFallbackImage(response.xml, variationInvId, viewInvId, baseUrl);
		if(imageUrl != null)
		{
			fallbackHtml = "<img src='" + imageUrl + "'/>";
		}
		else
		{
			fallbackHtml = "<span style='font-family: Verdana; font-size: 13px;'>Image Unavailable</span><br/><br/>";
			fallbackHtml += "<span style='font-family: Verdana; font-size: 11px;'>Could not find fall back image.</span>";
		}
	}
	else
	{
		fallbackHtml = "<span style='font-family: Verdana; font-size: 13px;'>Image Unavailable</span><br/><br/>";
		fallbackHtml += "<span style='font-family: Verdana; font-size: 11px;'>" + response.error + " </span>";
	}
	return fallbackHtml;
}

/**
 * Utility function for retrieving XML data. Used for the discovery 
 * of the fall back image and retrieving Display Control XML. Could be 
 * used for other purposes as well.
 * @return Object - An object describing the result of the request.
 * 				- success - Whether or not the request was successful
 * 				- xml - The XML document if the request was a success
 * 				- error - The error message if the request failed
 */
function executeSyncXmlRequest(url)
{
	var response = new Object();
	var http_request = null;

	if(window.XMLHttpRequest) // Mozilla, Safari,...
	{ 
		http_request = new XMLHttpRequest();
		if (http_request.overrideMimeType)
		{
			http_request.overrideMimeType('text/xml');
		}
	} 
	else if (window.ActiveXObject) // IE
	{
		try
		{
			http_request = new ActiveXObject("Msxml2.XMLHTTP");
		} 
		catch (e)
		{
			try
			{
				http_request = new ActiveXObject("Microsoft.XMLHTTP");
			} 
			catch (e)
			{
				// It'll get caught below
			}
		}
	}
	if(http_request == null)
	{
		response.success = false;
		response.error = "Could not create an XML HTTP Request";
	}
	else
	{
		http_request.open("GET", url, false);
		http_request.send(null);

		if(http_request.status == 200)
		{
			response.success = true;
			response.xml = http_request.responseXML;
		}
		else
		{
			response.success = false;
			response.error = "The request for " + url + " failed";
		}
	}
	
	return response;
}

/**
 * Turns a String of XML into an XML DOM object
 */
function getXmlFromString(xmlString)
{
	var doc;
	if (window.ActiveXObject) // IE
	{
		doc = new ActiveXObject("Microsoft.XMLDOM");
		doc.async = "false";
		doc.loadXML(xmlString);
	}
	else //  Mozilla, Firefox, Opera, etc.
	{
		var parser = new DOMParser();
		doc = parser.parseFromString(xmlString, "text/xml");
	}
	return doc.documentElement;
}

/**
 * Parses a Display XML file to find an appropriate fall back image
 * @param xml - The Product View / Display XML
 * @param variationId - The variation Id of the image to use. Only provided when displayControlXML is used.
 * @param viewId - The view Id of the image to use. Only provided when displayControlXML is used.
 * @param baseUrl - Used to load the template xml if the image is a config image
 * @return The URL to the fall back image. 
 */
function getFallbackImage(xml, variationInvId, viewInvId, baseUrl)
{
	var imageUrl;
	var variationId;
	var viewId;
	
	if(variationInvId != null || viewInvId != null)
	{
		var categoryGroups = xml.getElementsByTagName("category_group");
		
		for(var i = 0; i < categoryGroups.length; i++)
		{
			var categoryGroup = categoryGroups[i];
			if(categoryGroup.getAttribute("id") == "VARIATION" && variationInvId != null)
			{
				var categories = categoryGroup.getElementsByTagName("category");
				for(var j = 0; j < categories.length; j++)
				{
					var category = categories[j];
					if(category.getAttribute("inventory_id") == variationInvId)
					{
						variationId = category.getAttribute("identifier");
					}
				}
			}
			else if(categoryGroup.getAttribute("id") == "VIEW" && viewInvId != null)
			{
				var categories = categoryGroup.getElementsByTagName("category");
				for(var j = 0; j < categories.length; j++)
				{
					var category = categories[j];
					if(category.getAttribute("inventory_id") == viewInvId)
					{
						viewId = category.getAttribute("dentifier");
					}
				}
			}
		}
	}

	var matchingImageNode;
	var imageNodes = xml.getElementsByTagName("image");	
	for(var i = 0; i < imageNodes.length; i++)
	{
		var image = imageNodes[i];
		var categoryMappings = image.getElementsByTagName("category_mapping")
		var isMatch = true;
		
		for(var j = 0; j < categoryMappings.length; j++)
		{
			var mapping = categoryMappings[j];
			switch(mapping.getAttribute("group_id"))
			{
				case "IMAGETYPE":
					isMatch = (mapping.getAttribute("id") == "MAIN");
					break;
					
				case "VARIATION":
					if(variationId != null) isMatch = (variationId == mapping.getAttribute("id"));
					break;
					
				case "VIEW":
					if(viewId != null) isMatch = (viewId == mapping.getAttribute("id"));
					break;
			}
			if(!isMatch) break;
		}
		
		if(isMatch)
		{
			imageUrl = image.getAttribute("url");
			matchingImageNode = image;
			break;
		}
	}
	
	if(imageUrl.indexOf("concept://configure") == 0) imageUrl = getConfigImageFallback(matchingImageNode, xml, baseUrl, variationInvId, viewInvId);
	if(imageUrl.indexOf("http://") != 0) imageUrl = baseUrl + "standard/v2/swf/" + imageUrl;
	
	return imageUrl;
}

/**
 * Crafts a URL that is appropriate for loading a configurable image fall back
*/
function getConfigImageFallback(imageNode, displayXml, baseUrl, variationInvId, viewInvId)
{
	var imageUrl;
	
	// Load the template
	var templateXmlUrl = displayXml.getElementsByTagName("product_view")[0].getAttribute("viewer_xml_path");
	if(templateXmlUrl.indexOf("http://") != 0) templateXmlUrl = baseURL + "standard/v2/swf/" + templateXmlUrl;
	var response = executeSyncXmlRequest(templateXmlUrl);	
	if(response.success)
	{
		var templateXml = response.xml;
		var productViewer = templateXml.getElementsByTagName("product_viewer")[0];
		var hasViews = (productViewer.getAttribute("multiple_views") == "true") ? true : false;
		var hasVariations = (productViewer.getAttribute("multiple_variations") == "true") ? true : false;
	
		// If the variationInvId or viewInvId are not null we are using a specific image
		// if they are null we using the first main image we can find
		if(hasVariations)
		{
			var variationId = (variationInvId == null) ? findCategoryIdForConfigImageFallback("VARIATION", displayXml, imageNode) : variationInvId;
		}
		if(hasViews)
		{
			var viewId = (viewInvId == null) ? findCategoryIdForConfigImageFallback("VIEW", displayXml, imageNode) : viewInvId;
		}
		
		var swfUrl = imageNode.getAttribute("url").split("%2F").join("/");
		var urlToExtension = swfUrl.substring(0, swfUrl.indexOf(".swf"));
		var urlBase = swfUrl.substring(String("concept://configure?swfUrl=").length, urlToExtension.lastIndexOf("/"));
		
		var urlParts = swfUrl.split(".swf")[0].split("_");
		var size = urlParts[urlParts.length - 1];
		
		imageUrl = urlBase + "/main_";
		if(hasVariations) imageUrl += "variation_" + String(variationId).split(" ").join("-") + "_";
		if(hasViews) imageUrl += "view_" + String(viewId).split(" ").join("-") + "_";
		imageUrl += size + ".jpg";
	}

	return imageUrl;
}

function findCategoryIdForConfigImageFallback(viewOrVariation, xml, imageNode)
{
	// NOTE - Config fall back image names use either the inventory ID, if it exists or the 
	// the index of the variation / view category
	
	var id; // first get the view / variation id used to lookup the category
	var inventoryId; // we'll try to get an inventory id too, if one exists
	
	var mappings = imageNode.getElementsByTagName("category_mapping");
	for(var i = 0; i < mappings.length; i++)
	{
		var mapping = mappings[i];
		if(mapping.getAttribute("group_id") == viewOrVariation)
		{
			id = mapping.getAttribute("id");
			break;
		}
	}
	
	var categoryGroups = xml.getElementsByTagName("category_group");
	var categoryIndex;
	for(var j = 0; j < categoryGroups.length; j++)
	{
		var group = categoryGroups[j];
		if(group.getAttribute("id") == viewOrVariation)
		{
			var categories = group.getElementsByTagName("category");
			for(var k = 0; k < categories.length; k++)
			{
				var category = categories[k];
				if(category.getAttribute("id") == id)
				{
					inventoryId = category.getAttribute("inventory_id");
					categoryIndex = k + 1; // it's not 0 based
					break;
				}
			}
			break;
		}
	}
	return (inventoryId != null) ? inventoryId : categoryIndex;
}



