/**

 * Brochure.js

 * This is the root of all the objects in the brochure.

 * It interacts with the page by 3 major methods:

 * The constructor.

 * The writeInitialContent() method - called as the page is being written

 * The unLoad() method - called as part of onUnLoad.

 * All user interaction gets pumped through this object to be distributed after.

 *

 * The brochure has:

 * viewer - object that controls the main content and its interaction with the rest of the brochure.

 * template - responds to requests to generate HTML.

 * portal - The object representing the portal.

 * client - The object representing the client.

 * customizations - Loaded from brochureData to customize the template etc.

 * path - The path of the document base.

 * jsPath - The path to the directory holding JS file we're in.

 * browser - The type of browser we're being run in.

 * pages - List of page data objects looked up by name.

 * currentPageName - The name of the current page.

 * currentFloorplanName - The name of the current floorplan within the current page.

 *

 * The way it acquires the sub objects is via finding the JS file url in the brochureData structure.

 * This allows the HTML page to override which one is used before the Brochure object is created.

 * In the case of the Template, a constructor is needed.  So the name of the constructor is the same as

 * the filename.

 * So the HTML page follows the following rules:

 * 1) Include the JS files for the product (including this one)

 * 2) Include the brochureData js definition.

 * 3) Do whatever overrides are needed.

 * 4) Call the constructor of the Brochure passing in the data object and the path to the JS files.

 * 5) onLoad and onUnLoad are to wired up to the brochure object's load and unLoad functions.

 */



/*Static properties

 */

var clientJS = null;

var portalJS = null;

var templateJS = null;



/*******************************************************************************

 * Public methods.

 *******************************************************************************/



/**

 * Constructor.

 * @param jsPath - The relative or absolute URL from the document to the directory holding the javascript file we're in.

 * @param brochureDataPath - The path from this page to the brochureData

 * @param brochureData - The data structure to run our brochure.

 */

function Brochure( jsPath, brochureDataPath, brochureData )

{

	this.id = (new Date()).getTime();

		//Setup the cheap product defaults:

	this.useTemplateLayoutURL = true;

	this.layoutUrl = null;



		//Work out the path of the document we're in

	var path = document.location.href;

	var pos = path.lastIndexOf( '/' );

	if( pos > 0 )

	{

		path = path.substring( 0, pos );

	}

		//Remember the path we're in and the origin of the product JS files

	this.path = path;

	this.jsPath = jsPath;

		//Determine browser type so everything can look up here

	if( navigator.userAgent.indexOf("Opera") != -1 )

	{

		this.browser="Opera";

	}

	else if( document.all )

	{		//TODO better way to do this

		this.browser="IE";

	}

	else if( typeof( navigator.appName ) != "undefined" && navigator.appName.indexOf("Netscape") >= 0 )

	{

		this.browser="NN";

	}

	else

	{

		this.browser=null;

	}

		//Make sure all resources in brochure data are relative to the document path, not the brochureData path

	Utility.locateResources( brochureData, brochureDataPath, path );

		// Create the viewer.

	if (brochureData.viewer == 'Java')

	{

		//Perform cursory java check. More in depth testing occurs when the applet is being written to the page in JavaWrapper.js

		//This just checks that there is at least some form of java available.

		if(navigator.javaEnabled()) {

			this.viewer = new AppletWrapper(this);

		} else {

			//Check if a valid flash player is available

			var hasReqestedVersion = DetectFlashVer(7, 0, 0);

			if (hasReqestedVersion) {

				this.viewer = new FlashWrapper(this);

			} else {

				this.viewer = new NormalWrapper(this);

			}

		}

	}

	else if (brochureData.viewer == 'Flash')

	{

		//Check if a valid flash player is available

		var hasReqestedVersion = DetectFlashVer(7, 0, 0);

		if (hasReqestedVersion) {

			this.viewer = new FlashWrapper(this);

		} else {

			//Perform cursory java check. More in depth testing occurs when the applet is being written to the page in JavaWrapper.js

			//This just checks that there is at least some form of java available.

			if(navigator.javaEnabled()) {

				this.viewer = new AppletWrapper(this);

			} else {

				this.viewer = new NormalWrapper(this);

			}

		}

	}

	else

	{

			// If a viewer technology is not specified, use the Java viewer.

		this.viewer = new NormalWrapper(this);

	}



		// Load the pages.

	this.pages = new Array();

	for (var id in brochureData.pages)

	{

		var page = brochureData.pages[id];

		this.pages[id] = page;

	}



		// Load the customizations

	this.customizations = {};

	if( brochureData.customizations )

	{

		if( brochureData.customizations.viewerObject )

		{

			this.customizations.viewerObject = brochureData.customizations.viewerObject;

		}

		else

		{

			this.customizations.viewerObject = null;

		}

		if( brochureData.customizations.viewableTypes )

		{

			this.customizations.viewableTypes = brochureData.customizations.viewableTypes;

		}

		else

		{

			this.customizations.viewableTypes = null;

		}

	}

	else

	{

		this.customizations.viewerObject = [];

		this.customizations.viewableTypes = [];

	}

		//Now lets load in the client, portal and template

	if (!clientJS) {

		clientJS = brochureData.clientJS;

	}

	if (!portalJS) {

		portalJS = brochureData.portalJS;

	}

	if (!templateJS) {

		templateJS = brochureData.templateJS;

	}

	Utility.loadScript( clientJS );

	Utility.loadScript( portalJS );

	Utility.loadScript( templateJS );

		//TODO consider using Jon's generic system so we can extend this to load other object types

		// The brochure must always have a current page.

		//Set the initial page name

	this.currentPageName = brochureData.currentPageName;

		//Setup initial values

	this.client = null;

	this.portal = null;

	this.template = null;

}



/**

 * Sets the portal JS in the data structure found in the document

 */

Brochure.setPortal = function( portalJSV )

{

	portalJS = portalJSV;

}



/**

 * Sets the client JS in the data structure found in the document

 */

Brochure.setClient = function( clientJSV )

{

	clientJS = clientJSV;

}



/**

 * Sets the template JS in the data structure found in the document

 */

Brochure.setTemplate = function( templateJSV )

{

	templateJS = templateJSV;

}



/**

 * This is called in a separate tag on the web page so that the previous tags have run.

 * This primarily writes out the stylesheet tags

 */

Brochure.prototype.prepareHeadContent = function()

{

	this.templateClassName = Utility.getClassName( templateJS );

		//Since we have the clientData and portalData objects in the document.

		//We should now use them by first making the resources all link up and then using it

	Utility.locateResources( clientData, Utility.getDirectory( clientJS ), this.path );

	this.client = clientData;

	delete clientData;

	Utility.locateResources( portalData, Utility.getDirectory( portalJS ), this.path );

	this.portal = portalData;

	delete portalData;

	this.template = eval("new "+this.templateClassName+"()");

		//Tell the template to register its data

	this.template.init( Utility.getDirectory( templateJS ), this );

		// The brochureData object is only required during brochure

		// construction. After construction, the brochureData object

		// may be garbage-collected.

	delete brochureData;

}



/**

 * Called in a separate tag so that the prepareHeadContent can stream in other script tags.

 * This method writes out the style tags needed.  It also finishes the template initialisation.

 */

Brochure.prototype.finishHeadContent = function()

{

	this.template.finishInit();

		//Write out the stylesheets and remember for listening until they download

	var styleUrl = this.template.data.style;

	this.loadingStyleCSS = false;

	if( styleUrl != null )

	{

		this.loadingStyleCSS = true;

		Utility.loadStylesheet( styleUrl );

	}

	var layoutUrl;

	if( this.useTemplateLayoutURL )

	{

		layoutUrl = this.template.data.layout;

	}

	else

	{

		layoutUrl = this.layoutUrl;

	}

	this.loadingLayoutCSS = false;

	if( layoutUrl != null )

	{

			//Now start the CSS downloading

		this.loadingLayoutCSS = true;

		Utility.loadStylesheet( layoutUrl );

	}

}



/**

 * This should be called by as part of the initial page download.  NOT in onLoad.

 * At this point we should have window.clientData and window.portalData defined.

 * This method should write out the brochure DIVs.

 */

Brochure.prototype.writeInitialContent = function()

{

	var str = "";

	if (this.pages[this.currentPageName])

	{

		var pageName = this.currentPageName;

		var page = this.pages[pageName];

		this.currentFloorplanName = page.currentFloorplanName;

		this.viewer.loadPage(pageName,page);

		str += this.viewer.getInitialExternalContent();

		str += this.template.getInitialContent(pageName,page);

	}

	else

	{

		this.currentPageName = null;

		this.debug("Initial page doesn't exist!");

		str+="Error in brochureData - initial page doesn't exist!";

	}

	document.writeln(str);

}



/**

 * Called in response to the onLoad event.

 * This function sets up a callback loop that checks if the CSS files have downloaded using the <a  * href="#checkCSS">checkCSS()</a> function, and if so forwards execution to <a href="#onReady">onReady()</a>.

 */

Brochure.prototype.load = function()

{

		//Wait for stylesheets to download in a separate thread callback

	Utility.waitForCondition( this, "checkCSS", "onReady" );

}



/**

 * Callback for when the CSS downloads.  This method returns true if not waiting for the CSS to download or if certain style  * criteria are met.  Currently this is listening for #waitLayer {width:200px;height:100px;}.

 * Each of the two dimensions is defined in a separate CSS file.

 * If you replace or modify these files ensure that these styles are provided.  If not the brochure system will not continue.

 */

Brochure.prototype.checkCSS = function(count)

{

	if( this.loadingLayoutCSS )

	{

		var w = Utility.getStyle( "waitLayer", "width", "width" );

		var h = Utility.getStyle( "waitLayer", "height", "height" )

		var ans = (w == "200px");

		ans &= (h == "100px");

		return ans;

	}

	return true;

}



/**

 * Called when all external files have downloaded successfully, onLoad ran etc.

 * This launches the first rendering.

 * This is called by the Product extension to this Brochure.

 */

Brochure.prototype.onReady = function()

{

	var waitLayer = Brochure.getLayer("waitLayer");

	if( waitLayer != null )

	{

		waitLayer.style.display="none";

	}

	this.template.onReady();

}



/**

 * Called in response to the window's onUnload event.

 */

Brochure.prototype.unLoad = function()

{

		//Unload page

	this.unloadPage();

		//Tell the viewer to clean its part up

	this.viewer.unLoad();

		//Clear other objects

	this.viewer = null;

	this.customizations = null;

	this.pages = null;

	this.client = null;

	this.portal = null;

}



Brochure.prototype.getViewer = function()

{

	return this.viewer;

}



Brochure.prototype.getCustomizations = function()

{

	return this.customizations;

}



Brochure.prototype.getPortal = function()

{

	return this.portal;

}



Brochure.prototype.getClient = function()

{

	return this.client;

}



Brochure.prototype.getTemplate = function()

{

	return this.template;

}



/**

 * Returns an array containing all of the pages in this brochure.

 */

Brochure.prototype.getPageNames = function()

{

	var pageNames = new Array();



	for (var id in this.pages)

		pageNames.push(id);



	return pageNames;

}



/**

 * Gets the current page name.

 */

Brochure.prototype.getCurrentPageName = function()

{

	return this.currentPageName;

}



/**

 * Gets the current page.

 */

Brochure.prototype.getCurrentPage = function()

{

	return this.pages[this.currentPageName];

}



/**

 * Gets the page with the specified name.

 */

Brochure.prototype.getPage = function(pageName)

{

	return this.pages[pageName];

}



/**

 * Sets the current page to the specified value

 * unless the specified page does not exist.

 */

Brochure.prototype.changePage = function(pageName)

{

	this.unloadPage();

	if (this.pages[pageName])

	{

		this.currentPageName = pageName;

		var page = this.pages[pageName];

		this.currentFloorplanName = page.currentFloorplanName;

		this.viewer.loadPage(pageName,page);

		this.template.loadPage(pageName,page);

	}

}



/**

 * Called internally from two places

 */

Brochure.prototype.unloadPage = function()

{

	this.viewer.unloadPage();

	this.template.unloadPage();

}



/**

 * id is the index of the hotspot in the floorplan

 */

Brochure.prototype.floorPlanActionCallback = function( id )

{

	var fp = this.getCurrentFloorplan();

	if( fp == null )

	{

		return;

	}

	if( fp.HotspotsArray[id] )

	{

		var targetName = fp.HotspotsArray[id].targetName;

		if( typeof( this.getCurrentPage().floorplans[targetName] ) != "undefined" )

		{

			this.template.showFloorplan( targetName );

		}

		else if( typeof( this.getCurrentPage().viewables[targetName] ) != "undefined" )

		{

			this.viewer.showViewable( targetName );

		}

	}

}



/**

 * Called by the applet or flash to tell us that the viewable with the given id is now shown

 */

Brochure.prototype.notifyViewableChanged = function( id )

{

	this.viewer.notifyViewableChanged( id );

	this.template.notifyViewableChanged( id );

}



/**

 * Callback for a button press of some sort.

 * Buttons are defined with both a <em>type</em> and an <em>id</em>.

 * Types include &quot;thumbnail&quot;, &quot;floorplan&quot; and &quot;button&quot;.

 * Based on the type, one of three actions is undertaken:

 * <UL>

 * <LI>thumbnail - the <a href="Viewer.html#showViewable">Viewer.showViewable( id )</a> method is called.

 * <LI>floorplan - the <a href="Template.html#showFloorplan">Template.showFloorplan( id )</a> method is called.

 * <LI>button - the <a href="Template.html#buttonClicked">Template.buttonClicked( id )</a> method is called.

 * </UL>

 */

Brochure.prototype.buttonClicked = function( type, id )

{

	this.debug( "Brochure.buttonClicked( "+type+", "+id+" ) enter" );

	switch( type )

	{

	case 'thumbnail':

		this.viewer.showViewable( id );

		break;

	case 'floorplan':

		this.template.showFloorplan( id );

		break;

	case 'button':

		this.template.buttonClicked( id );

		break;

	default:

		this.debug("unknown button type clicked '"+type+"' id='"+id+"'");

	}

	this.debug("Brochure.buttonClicked( "+type+", "+id+" ) exit");

}



Brochure.prototype.mouseOverHandler = function( type, id )

{

	this.template.getButton( type, id ).mouseOverHandler();

}



Brochure.prototype.mouseOutHandler = function( type, id )

{

	this.template.getButton( type, id ).mouseOutHandler();

}



Brochure.prototype.getCurrentFloorplan = function()

{

	var page = this.getCurrentPage();

	if( page.floorplans )

	{

		if( typeof( page.floorplans[this.currentFloorplanName] ) != "undefined" )

		{

			return page.floorplans[this.currentFloorplanName];

		}

	}

	return null;

}



Brochure.prototype.setFloorplan = function(floorplanName)

{

	var page = this.getCurrentPage();

	if( page.floorplans && typeof( page.floorplans[floorplanName] ) != "undefined" )

	{

		this.currentFloorplanName = floorplanName;

	}

}



/**

 * Multi-browser way to get a DIV tag object TODO ensure it works with NN etc

 */

Brochure.getLayer = function( name )

{

	return document.getElementById( name );

}



/**

 * Due to some bugs in some browsers when we set innerHTML=<something new> and it doesn't really unload the

 * old content.  We have a way to force it to clear before setting the new value.

 */

Brochure.unloadDiv = function( divTag )

{

	if( divTag != null )

	{

		divTag.innerHTML = "";

	}

}



/**

 * Render one layer only.  This is called to render it again because it is already rendered in the overall rendering.

 * It can be passed parameters which are passed to the format method.

 */

Brochure.prototype.reRenderLayer = function( name, params )

{

	var layer = Brochure.getLayer( name );

	if( layer != null )

	{

		this.template.reRenderLayer( name, layer, params );

	}

}



/**

 * Shows the specified layer. If the layer overlaps with other layers,

 * the visibility of the other layers is set to hidden.

 */





 Brochure.prototype.showLayer = function(layerName)

 {



	// cross browser getStyle

	// http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/

	// supports

	// - IE 5.5+ 

	// - Firefox 

	// - Safari 

	// - Opera 8.5 

	// does not support IE5.0

	

	//TODO use/update Utility.getStyleFromTag()

	function getStyle(oElm, strCssRule){

	    var strValue = "";

	    if(document.defaultView && document.defaultView.getComputedStyle){

		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);

	    }

	    else if(oElm.currentStyle){

		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){

		    return p1.toUpperCase();

		});

		strValue = oElm.currentStyle[strCssRule];

	    }

	    return strValue;

	}



 	var layer = Brochure.getLayer(layerName);

 	if( layer == null )

 	{		//Wasn't there

 		return;

 	}

 		// Hide any layers that overlap with the layer.

 	var overlappingLayerNames = this.getOverlappingLayerNames(layerName);



 	var highestZ = 0;

 	var tempZ = 0;

 	var tStr = "";



 	for (var i = 0; i < overlappingLayerNames.length; i++)

 	{

 			//instead of hiding the other layer, we want to get the zIndex value and add one to it

 		//Brochure.getLayer(overlappingLayerNames[i]).style.visibility = 'hidden';

 		//tempZ = Brochure.getLayer(overlappingLayerNames[i]).style.zIndex;

 		var ber = document.getElementById(overlappingLayerNames[i]);

 		tempZ = getStyle(ber, "z-index");

 		tStr += overlappingLayerNames[i] + ".style.zindex:" + tempZ + "\n";

 		if (tempZ > highestZ) {

 			highestZ = tempZ;

 		}

 	}

 	highestZ ++;

 		// Show the layer.

 	layer.style.visibility = 'visible';

 	layer.style.zIndex = highestZ;

}



/* old version

Brochure.prototype.showLayer = function(layerName)

{

	var layer = Brochure.getLayer(layerName);

	if( layer == null )

	{		//Wasn't there

		return;

	}

		// Hide any layers that overlap with the layer.

	var overlappingLayerNames = this.getOverlappingLayerNames(layerName);



	for (var i = 0; i < overlappingLayerNames.length; i++)

	{

		Brochure.getLayer(overlappingLayerNames[i]).style.visibility = 'hidden';

	}



		// Show the layer.

	layer.style.visibility = 'visible';

}

*/



/**

 * Returns an array containing the names of all of the layers

 * that overlap with the layer whose name is provided as a parameter.

 */

Brochure.prototype.getOverlappingLayerNames = function(layerName)

{

	var overlappingLayerNames = new Array();

	var layers = this.template.getLayers();

	var layer = layers[layerName];



	for (var currentLayerName in layers)

	{

		var currentLayer = layers[currentLayerName];



		if (currentLayerName != layerName &&

			currentLayer.top < layer.bottom && currentLayer.bottom > layer.top &&

			currentLayer.left < layer.right && currentLayer.right > layer.left)

		{

			overlappingLayerNames.push(currentLayerName);

		}

	}



	return overlappingLayerNames;

}





/**

 * Returns a boolean informing if the given layer is on top of all other overlapping layers

 * true, its on top, false it is not on top, null if no such layer

 */



 Brochure.prototype.isLayerOnTop = function(layerName)

 {



	// cross browser getStyle

	// http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/

	// supports

	// - IE 5.5+ 

	// - Firefox 

	// - Safari 

	// - Opera 8.5 

	// does not support IE5.0

	

	//TODO use/update Utility.getStyleFromTag()

	function getStyle(oElm, strCssRule){

	    var strValue = "";

	    if(document.defaultView && document.defaultView.getComputedStyle){

		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);

	    }

	    else if(oElm.currentStyle){

		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){

		    return p1.toUpperCase();

		});

		strValue = oElm.currentStyle[strCssRule];

	    }

	    return strValue;

	}



 	var layer = Brochure.getLayer(layerName);

 	if( layer == null )

 	{		//Wasn't there

 		return null;

 	}

 		// Get overlapping layers.

 	var overlappingLayerNames = this.getOverlappingLayerNames(layerName);



 	var highestZ = 0;

 	var tempZ = 0;

 	var tStr = "";



 	for (var i = 0; i < overlappingLayerNames.length; i++)

 	{

 			//instead of hiding the other layer, we want to get the zIndex value and add one to it

 		//Brochure.getLayer(overlappingLayerNames[i]).style.visibility = 'hidden';

 		//tempZ = Brochure.getLayer(overlappingLayerNames[i]).style.zIndex;

 		var ber = document.getElementById(overlappingLayerNames[i]);

 		tempZ = getStyle(ber, "z-index");

 		tStr += overlappingLayerNames[i] + ".style.zindex:" + tempZ + "\n";

 		if (tempZ > highestZ) {

 			highestZ = tempZ;

 		}

 	}

	var concernedLayer = document.getElementById(layerName);

	tempZ = getStyle(concernedLayer, "z-index");

 	if (tempZ > highestZ ) return true;

 	else return false;

}



Brochure.prototype.debug = function(msg)

{

	var tag = document.getElementById('debugTA');

	if( tag )

	{

		tag.value+=msg+'\n';

		if( !this.debugText || this.debugText != "" )

		{

			this.debugText = "";

		}

	}

	else

	{

		if( !this.debugText )

		{

			this.debugText = "";

		}

		this.debugText+=msg+'\n';

	}

}



Brochure.prototype.isIE = function()

{

	return this.browser == "IE";

}



Brochure.prototype.isNN = function()

{

	return this.browser == "NN";

}



Brochure.prototype.isOpera = function()

{

	return this.browser == "Opera";

}


