/**
 * Template.js
 *
 * The template class manages all of the brochure's button data and provides
 * methods for formatting the HTML necessary to render these buttons.
 * In practice, only the formatRemoteToolbarImage() method will need to be
 * overridden in the descendant class. Most of this classes customization
 * is performed through the toolbarButtonData and templateButtonData objects.
 */

/**
 * This is the default data structure for a Template.
 * It is merged with the overrides when the real Template object calls registerData
 */
Template.BASE_DATA = {
	layers:
	{		//This defines the structure of the page in div tags.
		//each of these are evaluate to a corresponding function to get html for that frame
		//eg Client_Branding gets html from the function 'Template.prototype.getClient_BrandingContent = function(flag,params)'
		'Client_Branding': {},
		'Portal_Branding': {},
		'Action_Buttons': {},
		'templateButtonsLayer': {},
		'Main_Heading_Text': {},
		'Sub_Heading_Text': {},
		'General_Text': {},
		'Thumbnails_Frame': {},
		'Detailed_Image_Text': {},
		'Help_Frame': {},
		'Email_Frame': {},
		'viewerLayer': {},
		'View_Controls_Frame': {},
		'Floorplan': {},
		'Viewer_Frame':{},
		'External_Links':{},
		'Drop_Down_Menu':{},
		'Functions_Buttons': {},
		'User_Defined_Frame_1':{},
		'User_Defined_Frame_2':{},
		'User_Defined_Frame_3':{},
		'User_Defined_Frame_4':{},
		'User_Defined_Frame_5':{},
		'User_Defined_Frame_6':{},
		'User_Defined_Frame_7':{},
		'User_Defined_Frame_8':{}
		//'debugLayer':{}
	},
		//If non-null, after resource finding, this should be a string that is a URL to the style sheet
	style:null,
		//If non-null, after resource finding, this should be a string that is a URL to the CSS that defines the positioning of the layers
	layout:null,
		//This is a holder for everything to do with the remote toolbar stuff.
		//The format function is called to format the toolbar remote image.
		//The data parameter is this object itself
	remoteToolbar:{
		format:function(data){return "";}
	},
		//Each item in this array represents a button that exists once in the brochure
	buttonData:{
	},
		//This is one item that defines how to make floorplan buttons.
		//The constructor is called each timetha
	floorplanButtonData:{
		ctor:function(buttonType,data){}
	},
		//This is the default way to make icons on the floorplan surface.
		//After resource location, each item should be either null or an url
	iconData:{
		floorplan:{},
		pano:{},
		video:{},
		obj3d:{},
		still:{}
	},
		//This is how we define a thumbnail of a given type
	thumbnailData:{
		pano:{ctor:function(data,id,viewable){}},
		video:{ctor:function(data,id,viewable){}},
		still:{ctor:function(data,id,viewable){}},
		obj3d:{ctor:function(data,id,viewable){}}
	},

		//This is the default behaviour for a template - can be overridden in sub classes.
	viewerData:{
		'Java':{						//This whole sub object is ONLY for the Java viewer, not the flash viewer or other technologies
			'viewerObject':{			//Extra things for the applet
				attributes:{			//Attributes to be added to the APPLET tag
					codebase:{
						nodeType:"Resource",
						url:".."		//Important to not end with /
					},
					archive:"PanoViewer.zip"
				},
				callbackURL:{		//For Safari and maybe other browsers, we have to call out to another page
					nodeType:"Resource",	//and have that sent back to the browser.  This is that relative url.
					url:"../echo.html"
				},
				enablefiltering:true,	//Bilinear filtering is on by default
				incRate:50,				//50ms increment rate for redrawing non dragged animation
				revealHotSpots:true,	//Show all the hotspots
				logo:{					//The logo is found in the default place
					image:"$(brochure.portal.viewer_Logo_URL)"
				},
				onLoad:{				//When first loaded, callback to say that callouts work.  If the brochure doesn't get this message then either the viewer isn't working or it can't call out
					nodeType:"Callback",
					code:"function(){theBrochure.viewer.setCallbacksWorking(true);}"
				},
				onYawChange:{
					nodeType:"Callback",
					code:"function(yaw){theBrochure.template.notifyYawChanged(yaw);}"
				},
				onPitchChange:{
					nodeType:"Callback",
					code:"function(pitch){window.status=pitch;}"
				},
				onZoomChange:{
					nodeType:"Callback",
					code:"function(zoom){window.status=zoom;}"
				}
			},
			globalActions:{				//If no specific overrides are found, an action is looked up here
				'hide':null,
				'show':{
					nodeType:"LoadRoomAction",
					room:"$(v.v.indexInList)",		//v is the applet wrapper's object.  v.v is the Applet object.
					onFinish:{
						nodeType:"Callback",
						code:"function(){theBrochure.notifyViewableChanged('$(v.id)');}"
					}
				},
				'directedShow':"show",	//This is used by hotspot actions to show a viewable from a hotspot.
										//It allows the direction information in the hotspot to be used as part of the showing.
										//Default ignores it though
				'left':null,			//By default the standard actions do nothing
				'right':null,
				'zoomIn':null,
				'zoomOut':null,
				'thumbnail':{			//Called to move from one viewable to another when a thumbnail is clicked.
										//This is when there is nothing specific such as a hotspot.
										//PARAMS:
										//v - The from viewable
										//vType - The type of the from viewable
										//toV - The viewable to move to
										//toType - The type of the viewable to move to
										//cacheKey - The key used to cache with
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{			//We have 4 steps - do something at first, then hide, then show the other viewable, then do something in it
						vA_preHideThumbnail:{
							actionName:"preHideThumbnail",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(toV)",
								type:"$(toType)"
							}
						},
						vB_showOffThumbnail:{
							actionName:"showOffThumbnail",
							params:{
								v:"$(toV)",
								type:"$(toType)"
							}
						}
					}
				},
				'hotspotToURL':{							//Called when jumping to a URL outside
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{			//We have 2 steps - do something at first, then hide, then show the other viewable, then do something in it
						preHideHotspot:{
							actionName:"preHideHotspot",
							params:{
								v:"$(v)",
								type:"$(type)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						showURL:{
							actionName:"showURL",
							params:{
								url:"$(url)"
							}
						}
					}
				},
				'showURL':{
					nodeType:"LoadURLAction",
					url:"$(url)",
					frame:"_new"
				},
				'thumbnailWithHotspot':"hotspotNormal",
													//Called for a thumbnail when there is a hotspot.
													//PARAMS
													// v - The destination viewable, NOT the pano
													// vType - The destination viewable type
													// fromV - The pano viewable
													// cacheKey - The key to cache on
													//
													//Call the hotspotNormal by default.  IE we go to the other viewable and stay there.
				'stillHotspot':"hotspotReturn",
													//Call the hotspotNormal by default.  IE we go to the other viewable and stay there.
				'panoHotspot':"hotspotReturn",
													//Called for a hotspot from a pano to another viewable.
													//PARAMS
													// v - The destination viewable, NOT the pano
													// vType - The destination viewable type
													// fromV - The pano viewable
													// cacheKey - The key to cache on
													//
													//By default we return after showing a viewable.
													//Note that this action is attached to the TO viewable.
													//By default, the pano type overrides this so that when we move
													//from one pano to another via a hotspot we stay there.
				'hotspotNormal':{
					nodeType:"Script",				//Move via hotspot from a pano to the viewable and stay there
					loop:false,						//Not part of the spec, but it is included for convenience.
					interruptable:true,				//We run a script with four actions as laid out below
					actions:{
						panoV_preHideHotspot:{
							actionName:"preHideHotspot",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						panoV_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"directedShow",
							params:{
								v:"$(v)",
								type:"$(type)",
								hs:"$(hs)"
							}
						},
						vB_showOff:{
							actionName:"postShowHotspot",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				'hotspotReturn':{					//Move from a pano to another viewable by hotspot and then come back
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{
						panoV_preHideHotspot:{
							actionName:"preHideHotspotReturn",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						panoV_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"directedShow",
							params:{
								v:"$(v)",
								type:"$(type)",
								hs:"$(hs)"
							}
						},
						vB_showOff:{
							actionName:"postShowHotspotReturn",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_hide:{
							actionName:"hide",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						panoA_show:{
							actionName:"reShow",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)"
							}
						},
						panoA_postShowHotspotFinish:{
							actionName:"postShowHotspotFinish",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)"
							}
						}
					}
				},
				reShow:"show",							//Only pano cares about reShow
				tourTransition:{						//Guided tour to move from us to the next one
														//parameters:
														//v			Destination
														//type		Destination type
														//fromV
														//fromType
														//cacheKey
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTour",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_showOffTour:{
							actionName:"showOffTour",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				tourTransitionWithHotspot:{						//When moving in the guided tour from one viewable to
					nodeType:"Script",							//another and there is a hotspot
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTourWithHotspot",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_showOffTour:{
							actionName:"showOffTour",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				tourInit:null,				//If the guided tour starts from us
				tourStart:{
					nodeType:"Script",							//another and there is a hotspot
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTour",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				tourStartWithHotspot:{
					nodeType:"Script",							//another and there is a hotspot
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTourWithHotspot",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					},
					preHideThumbnail:null,
					showOffThumbnail:null,
					preHideHotspot:null,
					postShowHotspot:null,
					preHideTour:null,
					showOffTour:null,
					preHideTourWithHotspot:null,
					postShowHotspotReturn:null,
					postShowHotspotFinish:null,
					thumbnailWithHotspot:null
				}
			},
			viewableTypes:{			//This defines for each type of viewable the default attributes
				'pano':{			//The panorama type
					defaultRoomType:"SpheriCylinderRoom",
					hotspotType:"Centred3DHotspot",
					extra:{			//Extra things to add to every pano object
						hotspotBG:"#003366",
						hotspotFG:"white",
						topAngle:"15",
						bottomAngle:"-75"
					},
					excludeProps:{				//When adding extra stuff, don't copy these across from brochure data
						"type":true,			//This is always "pano" and identifies the viewable as such
						"description":true,		//The description
						"short_desc":true,		//The short description
						"thumbnailURL":true,	//The URL for the thumbnail
						"order":true,			//The order of the thumbnail
						"roomType":true,		//The type of room object to use - picked up in the specific code
						"hotspots":true			//We use this in specific code to make the PanoHotspots into Centred3DHotspots
					},
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							points:"(20,20)(20,-20)(-20,-20)(-20,20)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					actions:{					//The actions
						'left':"$(typeof(v.v.partial) == 'undefined' ? 'normalLeft' : 'partialLeft')",
						'partialLeft':{
							nodeType:"PingPongAction",
							time:60000,
							yaw:-360
						},
						'normalLeft':{
							nodeType:"PositionAction",
							time:60000,
							isRel:true,
							pos:{
								yaw:-360
							}
						},
						'right':"$(typeof(v.v.partial) == 'undefined' ? 'normalRight' : 'partialRight')",
						'partialRight':{
							nodeType:"PingPongAction",
							time:60000,
							yaw:360
						},
						'normalRight':{
							nodeType:"PositionAction",
							time:60000,
							isRel:true,
							pos:{
								yaw:360
							}
						},
						'zoomIn':{
							nodeType:"PositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:.5
							}
						},
						'zoomOut':{
							nodeType:"PositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:-.5
							}
						},
						'show':{
							nodeType:"LoadRoomAction",
							room:"$(v.v.indexInList)",		//v is the applet wrapper's object.  v.v is the Applet object.
							onFinish:{
								nodeType:"Callback",
								code:"function(){theBrochure.notifyViewableChanged('$(v.id)');}"
							},
							initPos:"$(typeof(v.v.initPos) == 'undefined' ? {yaw:0,pitch:0,zoom:1} : v.v.initPos)"		//TODO test this
						},
						'reShow':'show',
						tourInit:{				//When the tour begins in us
							nodeType:"PositionAction",
							isRel:false,
							pos:"$(v.v.initPos.yaw == undefined && v.v.initPos.pitch == undefined && v.v.initPos.zoom == undefined ? {yaw:0,pitch:0,zoom:1} : v.v.initPos)",		//TODO test this
							time:500		//Very quick
						},
						stillHotspot:'hotspotNormal',
						panoHotspot:'hotspotNormal',		//Pano to Pano means go to it and stay
						postShowHotspot:"postShowHotspotDefault",			//When there is a normal hotspot to us
						postShowHotspotReturn:"postShowHotspotDefault",		//When there is a hotspot to us that returns
						showOff:"left",					//Thumbnail from us to us
						showOffThumbnail:"left",		//Thumbnail from someone else to us after moving to us
						showOffTour:{
							nodeType:"PositionAction",
							time:7000,
							isRel:true,
							pos:{
								yaw:180
							}
						},
						directedShow:{
							nodeType:"LoadRoomAction",
							room:"$(v.v.indexInList)",		//v is the applet wrapper's object.  v.v is the Applet object.
							onFinish:{
								nodeType:"Callback",
								code:"function(){theBrochure.notifyViewableChanged('$(v.id)');}"
							},
							initPos:{
							  //yaw:"$(hs.pos.yaw)",			//This is in comments because the default is to just show the room.
							  //pitch:"$(hs.pos.pitch)",		//But if all the rooms are aligned then we can walk through from one to another
							  //zoom:"$(hs.pos.zoom)"			//This can be nicely changed to zoom into it too
							}
						},
						preHideThumbnail:"preHideDefault",			//Just a delegation
						preHideTour:"preHideDefault",				//Just a delegation
									//Specific to the pano
						preHideHotspot:"preHotspotDefault",
						preHideHotspotReturn:"preHotspotDefault",
						postShowHotspotFinish:"postShowDefault",
						preHideTourWithHotspot:"preHotspotDefault",
									//These are not part of the spec but just for delegation without customisation problems
						postShowHotspotDefault:{
							nodeType:"Script",
							loop:false,
							interruptable:true,
							actions:{
								postShow:"postShowDefault",
								showOff:"showOff"
							}
						},
						preHotspotDefaultHotSpotPositionAction:{
							nodeType:"HotSpotPositionAction",
							time:3000,
							hotspot:"$(hs.indexInList)"
						},
						preHotspotDefault:{
							nodeType:"Script",
							loop:false,
							interruptable:true,
							actions:{
								postShow:"preHotspotDefaultHotSpotPositionAction",
								showOff:"zoomIn"
							}
						},
						postShowDefault:null,
						preHideDefault:null
					}
				},
				'still':{
					defaultRoomType:"ImageLayer",
					hotspotType:"FlatHotSpot",
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							shape:"$(dataHS.shape)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					excludeProps:{
						"type":true,			//This is always "pano" and identifies the viewable as such
						"description":true,
						"short_desc":true,
						"thumbnailURL":true,
						"order":true
					},
					actions:{
						postShowHotspot:"postShowDefault",
						postShowHotspotReturn:"postShowDefault",
						showOff:null,
						showOffTour:{
							nodeType:"PauseAction",
							time:3000
						},
						showOffThumbnail:"postShowDefault",
						preHideThumbnail:null,
						preHideTour:null
					}
				},
				'video':{
					defaultRoomType:"ImageSequenceLayer",
					hotspotType:"FlatHotspot",
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							shape:"$(dataHS.shape)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					extra:{
						waitForFullImage:true,
						allowDragging:false,
						defaultTime:6000
					},
					excludeProps:{
						"type":true,
						"description":true,
						"short_desc":true,
						"thumbnailURL":true,
						"order":true
					},
					actions:{
						left:"play",
						right:"play",
						postShowHotspot:"play",
						postShowHotspotReturn:"play",
						showOff:"play",
						showOffTour:"play",
						showOffThumbnail:"play",
						preHideThumbnail:null,
						preHideTour:null,
								//Not part of spec
						play:{
							nodeType:"PlayImageSequenceAction",
							forward:true,
							respectTiming:true,
							loop:false,
							interruptable:true,
							rewindAtEnd:true,
							isLayer:false
						}
					}
				},
				'obj3d':{
					defaultRoomType:"ImageSequenceLayer",
					hotspotType:"FlatHotspot",
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							shape:"$(dataHS.shape)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					extras:{
						waitForFullImage:true,
						allowDragging:true,
						flush:false,
						defaultTime:300
					},
					excludeProps:{
						"type":true,
						"description":true,
						"short_desc":true,
						"thumbnailURL":true,
						"order":true
					},
					actions:{
						'zoomIn':{
							nodeType:"PositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:.5
							}
						},
						'zoomOut':{
							nodeType:"PositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:-.5
							}
						},					
						left:"play",
						right:"playBackwards",
						postShowHotspot:"play",
						postShowHotspotReturn:"play",
						showOff:"play",
						showOffTour:"play",
						showOffThumbnail:"play",
						preHideThumbnail:"preHideDefault",
						preHideTour:"preHideDefault",
								//Not part of spec
						'play':{
							nodeType:"PlayImageSequenceAction",
							forward:true,
							respectTiming:false,
							loop:true,
							interruptable:true,
							isLayer:false,
							rewindAtEnd:false			//TODO was in comments

						},
						'playBackwards':{
							nodeType:"PlayImageSequenceAction",
							forward:false,
							respectTiming:false,
							loop:true,
							interruptable:true,
							isLayer:false,
							rewindAtEnd:false			//TODO was in comments
						},
						preHideDefault:null
					}
				}
			}
		},
		'Flash':{						//This whole sub object is ONLY for the Java viewer, not the flash viewer or other technologies
			'viewerObject':{			//Extra things for the applet
				attributes:{			//Attributes to be added to the APPLET tag
					codebase:{
						nodeType:"Resource",
						url:"../"		//Important to not end with /
					},
					bgcolor:"#FFFFFF"
				},
				callbackURL:{		//For Safari and maybe other browsers, we have to call out to another page
					nodeType:"Resource",	//and have that sent back to the browser.  This is that relative url.
					url:"../echo.html"
				},
				enablefiltering:true,	//Bilinear filtering is on by default
				incRate:50,				//50ms increment rate for redrawing non dragged animation
				bgcolor:"#FFFFF",
				reveal_hotspots:true,	//Show all the hotspots
				logo:{					//The logo is found in the default place
					image:"<logo><image>"+"$(brochure.portal.logoURL)"+"</image><bgcolor></bgcolor></logo>"
				},
				onLoad:{				//When first loaded, callback to say that callouts work.  If the brochure doesn't get this message then either the viewer isn't working or it can't call out
					nodeType:"Callback",
					code:"function(){theBrochure.viewer.setCallbacksWorking(true);}"
				},
				onYawChange:{
					nodeType:"Callback",
					code:"function(yaw){ theBrochure.template.notifyYawChanged(yaw);}"
				},
				onPanoChange:{
					nodeType:"Callback",
					code:"function(){ }"
				},
				onPitchChange:{
					nodeType:"Callback",
					code:"function(pitch){window.status=pitch;}"
				},
				onZoomChange:{
					nodeType:"Callback",
					code:"function(zoom){window.status=zoom;}"
				}
			},
			globalActions:{				//If no specific overrides are found, an action is looked up here
				'hide':null,
				'show':{
					nodeType:"load_room",
					room:"$(v.v.indexInList)",		//v is the applet wrapper's object.  v.v is the Applet object.
					onFinish:{
						nodeType:"Callback",
						code:"function(){theBrochure.notifyViewableChanged('$(v.id)');}"
					}
				},
				'directedShow':"show",	//This is used by hotspot actions to show a viewable from a hotspot.
										//It allows the direction information in the hotspot to be used as part of the showing.
										//Default ignores it though
				'left':null,			//By default the standard actions do nothing
				'right':null,
				'zoomIn':null,
				'zoomOut':null,
				'thumbnail':{			//Called to move from one viewable to another when a thumbnail is clicked.
										//This is when there is nothing specific such as a hotspot.
										//PARAMS:
										//v - The from viewable
										//vType - The type of the from viewable
										//toV - The viewable to move to
										//toType - The type of the viewable to move to
										//cacheKey - The key used to cache with
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{			//We have 4 steps - do something at first, then hide, then show the other viewable, then do something in it
						vA_preHideThumbnail:{
							actionName:"preHideThumbnail",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(toV)",
								type:"$(toType)"
							}
						},
						vB_showOffThumbnail:{
							actionName:"showOffThumbnail",
							params:{
								v:"$(toV)",
								type:"$(toType)"
							}
						}
					}
				},
				'hotspotToURL':{							//Called when jumping to a URL outside
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{			//We have 2 steps - do something at first, then hide, then show the other viewable, then do something in it
						preHideHotspot:{
							actionName:"preHideHotspot",
							params:{
								v:"$(v)",
								type:"$(type)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						showURL:{
							actionName:"showURL",
							params:{
								url:"$(url)"
							}
						}
					}
				},
				'showURL':{
					nodeType:"LoadURLAction",
					url:"$(url)",
					frame:"_new"
				},
				'thumbnailWithHotspot':"hotspotNormal",
													//Called for a thumbnail when there is a hotspot.
													//PARAMS
													// v - The destination viewable, NOT the pano
													// vType - The destination viewable type
													// fromV - The pano viewable
													// cacheKey - The key to cache on
													//
													//Call the hotspotNormal by default.  IE we go to the other viewable and stay there.
				'hotspot':"hotspotReturn",
													//Called for a hotspot from a pano to another viewable.
													//PARAMS
													// v - The destination viewable, NOT the pano
													// vType - The destination viewable type
													// fromV - The pano viewable
													// cacheKey - The key to cache on
													//
													//By default we return after showing a viewable.
													//Note that this action is attached to the TO viewable.
													//By default, the pano type overrides this so that when we move
													//from one pano to another via a hotspot we stay there.
				'hotspotNormal':{
					nodeType:"Script",				//Move via hotspot from a pano to the viewable and stay there
					loop:false,						//Not part of the spec, but it is included for convenience.
					interruptable:true,				//We run a script with four actions as laid out below
					actions:{
						panoV_preHideHotspot:{
							actionName:"preHideHotspot",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						panoV_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"directedShow",
							params:{
								v:"$(v)",
								type:"$(type)",
								hs:"$(hs)"
							}
						},
						vB_showOff:{
							actionName:"postShowHotspot",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				'hotspotReturn':{					//Move from a pano to another viewable by hotspot and then come back
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{
						panoV_preHideHotspot:{
							actionName:"preHideHotspotReturn",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						panoV_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"directedShow",
							params:{
								v:"$(v)",
								type:"$(type)",
								hs:"$(hs)"
							}
						},
						vB_showOff:{
							actionName:"postShowHotspotReturn",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_hide:{
							actionName:"hide",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						panoA_show:{
							actionName:"reShow",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)"
							}
						},
						panoA_postShowHotspotFinish:{
							actionName:"postShowHotspotFinish",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)"
							}
						}
					}
				},
				reShow:"show",							//Only pano cares about reShow
				tourTransition:{						//Guided tour to move from us to the next one
														//parameters:
														//v			Destination
														//type		Destination type
														//fromV
														//fromType
														//cacheKey
					nodeType:"Script",
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTour",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_showOffTour:{
							actionName:"showOffTour",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				tourTransitionWithHotspot:{						//When moving in the guided tour from one viewable to
					nodeType:"Script",							//another and there is a hotspot
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTourWithHotspot",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						},
						vB_showOffTour:{
							actionName:"showOffTour",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				tourInit:null,				//If the guided tour starts from us
				tourStart:{
					nodeType:"Script",							//another and there is a hotspot
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTour",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				},
				tourStartWithHotspot:{
					nodeType:"Script",							//another and there is a hotspot
					loop:false,
					interruptable:true,
					actions:{
						vA_preHideTour:{
							actionName:"preHideTourWithHotspot",
							params:{
								v:"$(fromV)",
								type:"$(fromType)",
								hs:"$(hs)",
								cacheKey:"$(cacheKey)"
							}
						},
						vA_hide:{
							actionName:"hide",
							params:{
								v:"$(fromV)",
								type:"$(fromType)"
							}
						},
						vB_show:{
							actionName:"show",
							params:{
								v:"$(v)",
								type:"$(type)"
							}
						}
					}
				}
			},
			viewableTypes:{			//This defines for each type of viewable the default attributes
				'pano':{			//The panorama type
					defaultRoomType:"pano", //defaultRoomType:"3dpano",
					hotspotType:"Centred3DHotspot",
					extra:{			//Extra things to add to every pano object
						limit:"$(typeof(v.v.partial) == 'undefined' ? '' : '<yaw>'+v.v.partial+'</yaw>')",
						hotspotBG:"#003366",
						hotspotFG:"white",
						name:"$(v.v.short_desc)"
					},
					excludeProps:{				//When adding extra stuff, don't copy these across from brochure data
						"type":true,			//This is always "pano" and identifies the viewable as such
						"description":false,		//The description
						"short_desc":false,		//The short description
						"thumbnailURL":true,	//The URL for the thumbnail
						"order":true,			//The order of the thumbnail
						"roomType":true,		//The type of room object to use - picked up in the specific code
						"hotspots":false			//We use this in specific code to make the PanoHotspots into Centred3DHotspots
					},
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							points:"(20,20)(20,-20)(-20,-20)(-20,20)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					actions:{					//The actions
						'left':"$(typeof(v.v.partial) == 'undefined' ? 'normalLeft' : 'partialLeft')",
						'partialLeft':{
							nodeType:"PingPongAction",
							time:60000,
							yaw:-720
						},
						'normalLeft':{
							nodeType:"position",
							time:60000,
							relative:true,
								yaw:-720,
								pitch:0
						},
						'right':"$(typeof(v.v.partial) == 'undefined' ? 'normalRight' : 'partialRight')",
						'partialRight':{
							nodeType:"PingPongAction",
							time:60000,
							yaw:720
						},
						'normalRight':{
							nodeType:"position",
							time:60000,
							relative:true,
								yaw:720
						},
						'zoomIn':{
							nodeType:"position",
							time:4000,
							relative:true,
								zoom:.5
						},
						'zoomOut':{
							nodeType:"position",
							time:4000,
							relative:true,
								zoom:-.5
						},
						'show':{
							nodeType:"load_room",
							room:"$(v.v.indexInList)",		//v is the applet wrapper's object.  v.v is the Applet object.
							onFinish:{
								nodeType:"Callback",
								code:"function(){theBrochure.notifyViewableChanged('$(v.id)');}"
							},
							initPos:"$(typeof(v.v.initPos) == 'undefined' ? {yaw:0,pitch:0,zoom:1} : v.v.initPos)"		//TODO test this
						},
						'reShow':'show',
						tourInit:{				//When the tour begins in us
							nodeType:"position",
							relative:false,
							pos:"$(typeof(v.v.initPos) == 'undefined' ? {yaw:0,pitch:0,zoom:1} : v.v.initPos)",		//TODO test this
							time:1000		//Very quick
						},
						hotspot:'hotspotNormal',		//Pano to Pano means go to it and stay
						postShowHotspot:"postShowHotspotDefault",			//When there is a normal hotspot to us
						postShowHotspotReturn:"postShowHotspotDefault",		//When there is a hotspot to us that returns
						showOff:"left",					//Thumbnail from us to us
						showOffThumbnail:"left",		//Thumbnail from someone else to us after moving to us
						showOffTour:{
							nodeType:"position",
							time:7000,
							relative:true,
								yaw:180
						},
						directedShow:{
							nodeType:"load_room",
							room:"$(v.v.indexInList)",		//v is the applet wrapper's object.  v.v is the Applet object.
							onFinish:{
								nodeType:"Callback",
								code:"function(){theBrochure.notifyViewableChanged('$(v.id)');}"
							},
						  initPos:{
							  //yaw:"$(hs.pos.yaw)",			//This is in comments because the default is to just show the room.
							  //pitch:"$(hs.pos.pitch)",		//But if all the rooms are aligned then we can walk through from one to another
							  //zoom:"$(hs.pos.zoom)"			//This can be nicely changed to zoom into it too
						  }
						},
						preHideThumbnail:"preHideDefault",			//Just a delegation
						preHideTour:"preHideDefault",				//Just a delegation
									//Specific to the pano
						preHideHotspot:"preHotspotDefault",
						preHideHotspotReturn:"preHotspotDefault",
						postShowHotspotFinish:"postShowDefault",
						preHideTourWithHotspot:"preHotspotDefault",
									//These are not part of the spec but just for delegation without customisation problems
						postShowHotspotDefault:{
							nodeType:"Script",
							loop:false,
							interruptable:true,
							actions:{
								postShow:"postShowDefault",
								showOff:"showOff"
							}
						},
						preHotspotDefault:{
							nodeType:"HotSpotPosition",
							time:3000,
							hotspot:"$(hs.indexInList)"
						},
						postShowDefault:null,
						preHideDefault:null
					}
				},
				'still':{
					defaultRoomType:"still",
					hotspotType:"FlatHotSpot",
					extra:{
						name:"$(v.v.short_desc)"
					},
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							points:"$(dataHS.shape)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					excludeProps:{
						"type":true,			//This is always "pano" and identifies the viewable as such
						"description":false,
						"short_desc":false,
						"thumbnailURL":true,
						"order":true
					},
					actions:{
					        'zoomIn':{
					        	nodeType:"CUSTOMZoomInPositionAction",
					   		time:2000,
					   		isRel:true,
					   		pos:{
					   			zoom:.5
							}
						},
						'zoomOut':{
							nodeType:"CUSTOMZoomOutPositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:-.5
							}
						},
						postShowHotspot:"postShowDefault",
						postShowHotspotReturn:"postShowDefault",
						showOff:null,
						showOffTour:{
							nodeType:"position",
							time:3000
						},
						showOffThumbnail:"postShowDefault",
						preHideThumbnail:null,
						preHideTour:null
					}
				},
				'video':{
					defaultRoomType:"slideshow",
					hotspotType:"FlatHotspot",
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							points:"$(dataHS.shape)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					extra:{
						initial_action:'3',
						waitForFullImage:true,
						allowDragging:false,
						defaultTime:6000,
						name:"$(v.v.short_desc)"
					},
					excludeProps:{
						type:true,
						"description":false,
						"short_desc":false,
						"thumbnailURL":true,
						"order":true
					},
					actions:{
						left:"play",
						right:"play",
						postShowHotspot:"play",
						postShowHotspotReturn:"play",
						showOff:"play",
						showOffTour:"play",
						showOffThumbnail:"play",
						preHideThumbnail:null,
						preHideTour:null,
								//Not part of spec
						play:{
							nodeType:"PlayImageSequenceAction",
							forward:true,
							respectTiming:true,
							loop:false,
							interruptable:true,
							rewindAtEnd:true,
							isLayer:false
						},
						'zoomOut':{
							nodeType:"position",
							time:2000,
							relative:true,
								zoom:-.5
						},
						'zoomIn':{
							nodeType:"position",
							time:2000,
							relative:true,
								zoom:.5
						}
					}
				},
				'obj3d':{
					defaultRoomType:"3dobject",
					hotspotType:"FlatHotspot",
					allHotspots:{				//Definition of every hotspot in this pano
						extras:{				//Adds to every hotspot as default values overridable in the real template and brochure
							points:"$(dataHS.shape)",
							text:"$(page.viewables[dataHS.targetViewableName].short_desc)"
						},
						excludeProps:{			//When copying a hotspot across, what do we exclude?
							targetViewableName:true,//This is the viewable name (key in the viewables array) to go to
							url:true			//This is for when we link to a URL instead
						}
					},
					extras:{
						waitForFullImage:true,
						allowDragging:true,
						flush:false,
						defaultTime:300,
						name:"$(v.v.short_desc)"
					},
					excludeProps:{
						type:true,
						"description":false,
						"short_desc":false,
						"thumbnailURL":true,
						"order":true
					},
					actions:{
						'zoomIn':{
							nodeType:"CUSTOMZoomInPositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:.5
							}
						},
						'zoomOut':{
							nodeType:"CUSTOMZoomOutPositionAction",
							time:2000,
							isRel:true,
							pos:{
								zoom:-.5
							}
						},
						left:"play",
						right:"playBackwards",
						postShowHotspot:"play",
						postShowHotspotReturn:"play",
						showOff:"play",
						showOffTour:"play",
						showOffThumbnail:"play",
						preHideThumbnail:"preHideDefault",
						preHideTour:"preHideDefault",
								//Not part of spec
						'play':{
							nodeType:"PlayImageSequenceAction",
							forward:true,
							respectTiming:false,
							loop:true,
							interruptable:true,
							isLayer:false,
							rewindAtEnd:false			//TODO was in comments

						},
						'playBackwards':{
							nodeType:"PlayImageSequenceAction",
							forward:false,
							respectTiming:false,
							loop:true,
							interruptable:true,
							isLayer:false,
							rewindAtEnd:false			//TODO was in comments
						},
						preHideDefault:null
					}
				}
			}
		}
	}
};

/**
 * This is optionally used by subclasses as default places to find images relative to the sub class, not this class.
 */
Template.DEFAULT_DATA = {
	style:{nodeType:"Resource",url:'look.css'},
	layout:{nodeType:"Resource",url:'layout.css'},
	remoteToolbar:{
		img:{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
		// Function deprecated 20060515 (jamie)
		/*
		format:function(data){
			return '<img name="toolbarRemote" '
				+ 'height="39" '
				+ 'width="123" '
				+ 'src="' + data.img + '">';
		}
		*/
		// New function
		format:function(data){
			return '<img name="toolbarRemote" '
				+ 'src="' + data.img + '">';
		}
	},
	iconData:{
		floorplan:{nodeType:"Resource",url:"Images/icon_floorplan_floorplan.gif"},
		pano:{nodeType:"Resource",url:"Images/pano-ani.gif"},
		video:{nodeType:"Resource",url:"Images/film-ani.gif"},
		obj3d:{nodeType:"Resource",url:"Images/object-ani.gif"},
		still:{nodeType:"Resource",url:"Images/still-ani.gif"}
	},
	buttonData:{
		mute:
		{
			ctor:		MuteButton,
			image:		{nodeType:"Resource",url:'Images/toolbar_nosound.gif'},
			rolloverImage:	{nodeType:"Resource",url:'Images/toolbar_nosound_over.gif'},
			muteImage:		{nodeType:"Resource",url:'Images/toolbar_mute.gif'},
			rolloverMuteImage:{nodeType:"Resource",url:'Images/toolbar_mute_over.gif'},
			activeImage:		{nodeType:"Resource",url:'Images/toolbar_activeSound.gif'},
			rolloverActiveImage:{nodeType:"Resource",url:'Images/toolbar_activeSound_over.gif'},
			width:		33,
			height:		39,
			remoteId:	'toolbarRemote',
			remoteWidth:	123,
			remoteHeight:	39,
			remoteImage:       	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_mute.gif'},
			actions:{
				mute:"viewer"
			}
		},
			// Toolbar button data.
		zoomOut:
		{
			ctor:		RemoteButton,
			image:		{nodeType:"Resource",url:'Images/toolbar_zoom_out.gif'},
			rolloverImage:	{nodeType:"Resource",url:'Images/toolbar_zoom_out_over.gif'},
			width:		33,
			height:		39,
			remoteId:	'toolbarRemote',
			remoteWidth:	123,
			remoteHeight:	39,
			remoteImage:       	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_zoom_out.gif'},
			actions:{
				zoomOut:"viewer"
			}
		},
		zoomIn:
		{
			ctor:		RemoteButton,
			image:		{nodeType:"Resource",url:'Images/toolbar_zoom_in.gif'},
			rolloverImage:	{nodeType:"Resource",url:'Images/toolbar_zoom_in_over.gif'},
			width:		33,
			height:		39,
			remoteId:	'toolbarRemote',
			remoteWidth:	123,
			remoteHeight:	39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_zoom_in.gif'},
			actions:{
				zoomIn:"viewer"
			}
		},
		help:
		{
			ctor:		RemoteButton,
			image:		{nodeType:"Resource",url:'Images/toolbar_help.gif'},
			rolloverImage:	{nodeType:"Resource",url:'Images/toolbar_help_over.gif'},
			width:		33,
			height:		39,
			remoteId:	'toolbarRemote',
			remoteWidth:	123,
			remoteHeight:	39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_help.gif'},
			actions:{
					showHelp_Frame:"template"
			}
		},
		left:
		{
			ctor:			RemoteButton,
			image:			{nodeType:"Resource",url:'Images/toolbar_move_left.gif'},
			rolloverImage:		{nodeType:"Resource",url:'Images/toolbar_move_left_over.gif'},
			width:			33,
			height:			39,
			remoteId:		'toolbarRemote',
			remoteWidth:		123,
			remoteHeight:		39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_move_left.gif'},
			actions:{
				left:"viewer"
			}
		},
		stop:
		{
			ctor:					RemoteButton,
			image:					{nodeType:"Resource",url:'Images/toolbar_stop.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/toolbar_stop_over.gif'},
			width:							33,
			height:							39,
			remoteId:						'toolbarRemote',
			remoteWidth:					123,
			remoteHeight:					39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_stop.gif'},
			actions:{				stop:"viewer"
			}
		},
		right:
		{
			ctor:					RemoteButton,
			image:					{nodeType:"Resource",url:'Images/toolbar_move_right.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/toolbar_move_right_over.gif'},
			width:							33,
			height:							39,
			remoteId:						'toolbarRemote',
			remoteWidth:					123,
			remoteHeight:					39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_move_right.gif'},
			actions:{
				right:"viewer"
			}
		},
		enlarge:
		{
			ctor:					RemoteButton,
			image:					{nodeType:"Resource",url:'Images/toolbar_enlarge.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/toolbar_enlarge_over.gif'},
			width:							33,
			height:							39,
			remoteId:						'toolbarRemote',
			remoteWidth:					123,
			remoteHeight:					39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_enlarge.gif'},
			actions:{
				enlargeViewer:"template"
			}
		},
		toolbarGuidedTour:
		{
			ctor:					RemoteButton,
			image:					{nodeType:"Resource",url:'Images/toolbar_guided_tour.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/toolbar_guided_tour_over.gif'},
			width:							33,
			height:							39,
			remoteId:						'toolbarRemote',
			remoteWidth:					123,
			remoteHeight:					39,
			remoteImage:        	{nodeType:"Resource",url:'Images/toolbar_remote_default.gif'},
			remoteRolloverImage:   	{nodeType:"Resource",url:'Images/toolbar_remote_guided_tour.gif'},
			actions:{
				guidedTour:"viewer"
			}
		},
			// Template button data.
		overview:
		{
			ctor:					Button,
			image:					{nodeType:"Resource",url:'Images/template_overview.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/template_overview_over.gif'},
			width:							66,
			height:							18,
			actions:{
				showGeneral_Text:"template"
			}
		},
		templateGuidedTour:
		{
			ctor:					Button,
			image:					{nodeType:"Resource",url:'Images/template_movie.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/template_movie_over.gif'},
			width:							66,
			height:							18,
			actions:{
				guidedTour:"viewer"
			}
		},
		print:
		{
			ctor:					Button,
			image:					{nodeType:"Resource",url:'Images/template_print.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/template_print_over.gif'},
			width:							66,
			height:							18,
			actions:{
				showPrintDialog:"template"
			}
		},
		email:
		{
			ctor:					Button,
			image:					{nodeType:"Resource",url:'Images/template_email.gif'},
			rolloverImage:			{nodeType:"Resource",url:'Images/template_email_over.gif'},
			width:							66,
			height:							18,
			actions:{
				/*showEmail_Frame:"template"*/
			}
		}
	},

	// The floorplan button entry should be thought of as a pattern for
	// creating as many floorplan buttons as are required at runtime.
	// In other words, don't declare multiple floorplan buttons.
	floorplanButtonData:{
		ctor:					Button,
		//image:					{nodeType:"Resource",url:'Images/icon_plan.gif'},
		//rolloverImage:			{nodeType:"Resource",url:'Images/icon_plan_on.gif'},
		image:					{nodeType:"Resource",url:'Images/e_floorplan.gif'},
		rolloverImage:			{nodeType:"Resource",url:'Images/e_floorplan_on.gif'},
		width:							66,
		height:							18
	},
	thumbnailData:{
		// Like the floorplan buttons, the thumbnail entries should also be
		// thought of as patterns for creating objects. In other words, it is
		// not necessary to define multiple panoramaThumbnail entries because a
		// particular tour contains two or more panoramas. Instead, just one
		// panoramaThumbnail entry should be defined and the software will create
		// as many panoramaThumbnail instances as are required.
		pano:
		{
			ctor:	PanoramaThumbnail,
			icon:			{nodeType:"Resource",url:'Images/icon_pano.gif'},
			iconAni:		{nodeType:"Resource",url:'Images/icon_pano_on.gif'},
			outerWidth:		70,
			outerHeight:	40,
			titleWidth:		70,
			titleHeight:	16
		},
		video:
		{
			ctor:	Thumbnail,
			icon:			{nodeType:"Resource",url:'Images/icon_video.gif'},
			iconAni:		{nodeType:"Resource",url:'Images/icon_video_on.gif'},
			outerWidth:		70,
			outerHeight:	40,
			titleWidth:		70,
			titleHeight:	16
		},
		obj3d:
		{
			ctor:	Thumbnail,
			icon:			{nodeType:"Resource",url:'Images/icon_object3D.gif'},
			iconAni:		{nodeType:"Resource",url:'Images/icon_object3D_on.gif'},
			outerWidth:		70,
			outerHeight:	40,
			titleWidth:		70,
			titleHeight:	16
		},
		still:
		{
			ctor:	Thumbnail,
			icon:			{nodeType:"Resource",url:'Images/icon_2d.gif'},
			iconAni:		{nodeType:"Resource",url:'Images/icon_2d_on.gif'},
			outerWidth:		70,
			outerHeight:	40,
			titleWidth:		70,
			titleHeight:	16
		}
	},
	viewerData:{
		'Java':{
			viewableTypes:{
				'pano':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_panorama.gif"
						}
					}
				},
				'still':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_still.gif"
						}
					}
				},
				'video':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_animated_seq.gif"
						}
					}
				},
				'obj3d':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_3dobject.gif"
						}
					}
				}
			}
		},
		'Flash':{
			viewableTypes:{
				'pano':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_panorama.gif"
						}
					}
				},
				'still':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_still.gif"
						}
					}
				},
				'video':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_animated_seq.gif"
						}
					}
				},
				'obj3d':{
					hotspotExtras:{		//Add to a hotspot that points to us
						image:{
							nodeType:"Resource",
							url:"Images/icon_floorplan_3dobject.gif"
						}
					}
				}
			}
		}
	}
};

/**
 * Constructor.
 */
function Template()
{
}

/**
 * Called by the brochure object to load.
 * This should call registerData somehow if it is overridden.
 */
Template.prototype.init = function(path,brochure)
{
	Utility.locateResources( Template.BASE_DATA, brochure.jsPath, brochure.path );
	Utility.locateResources( Template.DEFAULT_DATA, path, brochure.path );
	this.registerDatas( path, brochure, [Template.DEFAULT_DATA,Template.BASE_DATA] );
}

/**
 * If we use all the main default values for images etc BUT RELATIVE TO THE CUSTOMIZED BROCHURE.
 */
Template.prototype.registerUsingDefaultData = function( path, brochure, data )
{
	Utility.locateResources( Template.BASE_DATA, brochure.jsPath, brochure.path );
	Utility.locateResources( Template.DEFAULT_DATA, path, brochure.path );
	Utility.locateResources( data, path, brochure.path );
	this.registerDatas( path, brochure, [data,Template.DEFAULT_DATA,Template.BASE_DATA] );
}

/**
 * Called by the sub class when it is ready to setup the information on which buttons exist and how to format them.
 * The buttonData object is a map of button id to buttonDatum.
 * buttonDatum contains the information to send to the button constructor.
 * The only thing it needs is for sure is the attribute "constructor" which is a function
 * that will be called to construct the new button object passing the buttonDatum.
 * The buttonDatum has the id set in it for the button's usage.
 * All the constructed buttons are put into the array buttons owned by the template.
 *
 * The floorplan is the template for writing floorplan buttons.
 *
 * The thumbnails are different in that we have templates and not the final thumbnails.
 * For each type of viewable we have a thumbnail type.
 */
Template.prototype.registerData = function( path, brochure, data )
{
	Utility.locateResources( Template.BASE_DATA, brochure.jsPath, brochure.path );
	Utility.locateResources( data, path, brochure.path );
	this.registerDatas( path, brochure, [data,Template.BASE_DATA] );
}

/**
 * Called internally by one of the above 3 methods.
 * Builds one common data object from the given data objects.
 * Sets up the internal data structures.
 */
Template.prototype.registerDatas = function( path, brochure, datas )
{
	this.brochure = brochure;
	this.datas = datas;
	this.path = path;
}

/**
 * This is called from the Brochure.writeInitialContent() method.
 * The point is that all the various scripts needed have now loaded and the configuration object can be built.
 */
Template.prototype.finishInit = function()
{
		//Now we merge the datas objects into one big configuration object that we store under data
	this.data = {};
	Utility.addIntoNodeN( this.data, this.datas, null, null, false, null );
	delete this.datas;

		//Now clear out properties ready to build stuff
	this.callbacks = null;
	this.buttons = new Object();
	this.floorplans = new Object();
	this.thumbnails = new Object();
	this.thumbnailsByOrder = new Object();

		// Create the toolbar and other standard buttons and store them in buttons array
	this.viewerActions = [];
	for (var id in this.data.buttonData)
	{
		var buttonDatum = this.data.buttonData[id];
		if( typeof( buttonDatum.actions ) == "undefined" || buttonDatum.actions == null )
		{
			buttonDatum.actions = {};
		}
		else
		{
			for( var actionID in buttonDatum.actions )
			{
				if( buttonDatum.actions[actionID] == "viewer" )
				{
					this.viewerActions.push(actionID);
				}
			}
		}
		buttonDatum.id = id;
		this.buttons[id] = new buttonDatum.ctor("button",buttonDatum);
	}
}

/**
 * Called when onReady on the brochure has happened because the CSS files are downloaded
 */
Template.prototype.onReady = function()
{
	for (var layerName in this.data.layers)
	{
		var layer	= this.data.layers[layerName];
		if( layer != null )
		{
			var layerTag = document.getElementById(layerName);
			if( layerTag == null )
			{
				this.data.layers[layerName]=null;
			}
			else
			{
				layer.top = parseInt(Utility.getStyle(layerName, 'top', 'top'));
				layer.left = parseInt(Utility.getStyle(layerName, 'left', 'left'));
				layer.right = layer.left + parseInt(Utility.getStyle(layerName, 'width', 'width'));
				layer.bottom = layer.top + parseInt(Utility.getStyle(layerName, 'height', 'height'));
			}
		}
	}
	for( var name in this.data.layers )
	{
		if( this.data.layers[name] != null )
		{
			var layer = Brochure.getLayer( name );
			if( layer != null )
			{
				var propName = name.charAt(0).toUpperCase()+name.substring(1);
				var funcName = "format"+propName;
				if( typeof( this[funcName] ) == "function" &&
				    typeof( this["get"+propName+"Content"] ) != "function" )
				{		//It has a format method and neither of the other two
					this[funcName]( layer, false, null );
				}
			}
		}
	}
	this.endFormat();
}

/**
 * Called to unload the page-specific information
 */
Template.prototype.unloadPage = function()
{
	this.thumbnails = null;
	this.thumbnailsByOrder = null;
	this.floorplans = null;
	this.pageName = null;
	for( var name in this.data.layers )
	{
		var funcName = "unload"+name.charAt(0).toUpperCase()+name.substring(1);
		if( typeof( this[funcName] ) == "function" )
		{
			this[funcName]();
		}
		else
		{
			Brochure.unloadDiv(Brochure.getLayer(name));
		}
	}
}

Template.prototype.nonVisualLoadPage = function( pageName, page )
{
	this.thumbnails = new Object();
	this.thumbnailsByOrder = new Object();
	this.pageName = pageName;


	for (var id in page.viewables)
	{
		var viewable = page.viewables[id];
		var thumbnailDef = this.data.thumbnailData[viewable.type];
		this.thumbnailsByOrder[viewable.order] = this.thumbnails[id] = new thumbnailDef.ctor(thumbnailDef, id, viewable);
	}

	this.floorplans = new Object();
	var floorplans = page.floorplans;
	for( var id in floorplans )
	{
		var floorplan = floorplans[id];
		this.data.floorplanButtonData.id = id;
		this.data.floorplanButtonData.alt = floorplan.alt;
		this.floorplans[id] = new this.data.floorplanButtonData.ctor("floorplan",this.data.floorplanButtonData);
		delete this.data.floorplanButtonData.id;
		delete this.data.floorplanButtonData.alt;
	}
	this.resetButtonStates();
}

Template.prototype.getInitialContent = function(pageName,page)
{
	this.brochure.debug("Starting getInitialContent");
	this.nonVisualLoadPage( pageName, page );
	this.startFormat();
	var str = "";
		//Write out the layers
	str += "<div id=\"brochureLayer\" style=\"display:block\">";
	str += "<div id=\"waitLayer\"></DIV>";
		// Create each of the inner layers and append them to the enclosing layer.
	for (var layerName in this.getLayers())
	{		// Get the existing layer
		str+="<div id=\""+layerName+"\"";
		var propName = layerName.charAt(0).toUpperCase()+layerName.substring(1);
		var funcName = "getInitial"+propName+"Content";
		var ans;
		if( typeof( this[funcName] ) == "function" && (ans = this[funcName]()) != null )
		{
			str+=ans;
		}
		else
		{
			str+=">";
			funcName = "get"+propName+"Content";
			if( typeof( this[funcName] ) == "function" && (ans = this[funcName](false,null)) != null )
			{
				str+=ans;
			}
		}
		str+="</div>";
	}
	str+="</div>";
	return str;
}

/**
 * Called when the page changes to load in the new button etc details into the template
 */
Template.prototype.loadPage = function(pageName,page)
{
	this.nonVisualLoadPage( pageName, page );
	this.startFormat();
	for( var name in this.data.layers )
	{
		if( this.data.layers[name] != null )
		{
			var layer = Brochure.getLayer( name );
			if( layer != null )
			{
				var propName = name.charAt(0).toUpperCase()+name.substring(1);
				var funcName = "get"+propName+"Content";
				if( typeof( this[funcName] ) == "function" )
				{
					var str = this[funcName](false,null);
					if( str != null )
					{
						layer.innerHTML = "";
						layer.innerHTML = str;
					}
				}
				else
				{
					funcName = "format"+propName;
					if( typeof( this[funcName] ) == "function" )
					{
						this[funcName]( layer, false, null );
					}
				}
			}
		}
	}

	this.endFormat();
}

Template.prototype.reRenderLayer = function( name, layer, params )
{
	var propName = name.charAt(0).toUpperCase()+name.substring(1);
	var funcName = "get"+propName+"Content";
	if( typeof( this[funcName] ) == "function" )
	{
		layer.innerHTML = "";
		layer.innerHTML = this[funcName](true,params);
	}
	else
	{
		funcName = "format"+name.charAt(0).toUpperCase()+name.substring(1);
		if( typeof( this[funcName] ) == "function" )
		{
			this.startFormat();
			this[funcName]( layer, true, params );
			this.endFormat();
		}
	}
}

/**
 * This returns the data structure used to customize the viewer.
 */
Template.prototype.getViewerData = function( type )
{
	return this.data.viewerData[type];
}

/**
 * This returns the list of action IDs that are expected to exist on the viewer
 */
Template.prototype.getViewerActions = function()
{
	return this.viewerActions;
}

Template.prototype.getFormattedButton = function(id)
{
	if (this.buttons[id])
	{
		return this.buttons[id].format();
	}
	else
	{
		return null;
	}
}

Template.prototype.getFormattedImage = function(id)
{
	if (this.buttons[id])
	{
		return this.buttons[id].format();
	}
	else
	{
		return null;
	}
}

Template.prototype.getUnformattedImage = function(id)
{
	if (this.buttons[id])
	{
		return this.buttons[id].raw();
	}
	else
	{
		return null;
	}
}

Template.prototype.startFormat = function()
{
	this.brochure.debug("Starting format");
	this.callbacks = new Array();
}

Template.prototype.addAfterFormatCallback = function( obj )
{
	this.callbacks.push( obj );
}

Template.prototype.endFormat = function()
{
	this.brochure.debug("Ending format");
	var callbacks = this.callbacks;
	this.callbacks = null;
	if(callbacks) {
		for( var i = 0; i < callbacks.length; i++ )
		{
			callbacks[i].onFormatFinished();
		}
	}
}

/**
 * Formats and shows the floorplan layer.
 *
 * The sourceButtonId parameter contains the name of the button
 * that generated the click event. The button name will be
 * something like 'floorplan1', 'floorplan2' or 'floorplan3'.
 * The showFloorplan method uses the button name to lookup
 * the floorplan layer to be displayed. Consequently, care should
 * be taken to ensure that the floorplans specified in brochureData.js
 * are identified by the names 'floorplan1', 'floorplan2', etc.
 */
Template.prototype.showFloorplan = function(id)
{		//TODO When we have a shortcut type bar to choose the floorplan from, do it there too.
	this.brochure.setFloorplan(id);
	if( this.getLayers()['Floorplan'] != null )
	{
		this.brochure.reRenderLayer( 'Floorplan', this.brochure.getCurrentPage().currentViewableName );
		this.brochure.showLayer('Floorplan');
		if(this.floorplanDirection != null) {this.floorplanDirection.clear();this.floorplanDirection=null;}
		this.floorplanDirection = new jsGraphics("FloorplanDirection");		
		_jg = this.floorplanDirection;
	}
}

Template.prototype.resetButtonStates = function()
{
	var viewer = this.brochure.viewer;
	for( var i in this.buttons )
	{
		var bid;
		switch( i )
		{
		case 'toolbarGuidedTour':		//TODO hacky
		case 'templateGuidedTour':
			bid = 'guidedTour';
			break;
		default:
			bid = i;
		}
		var state = viewer.getButtonState( bid );
		if( state != null )
		{
			this.buttons[i].setEnabled( state );
		}
	}
}

/**
 * The get...IconURL() methods return useful default values.
 * The main difference between the old brochure architecture
 * and the new brochure architecture in this context, is that
 * the new architecture enables the defaults to be overridden
 * in descendant classes.
 */
Template.prototype.getFloorplanIconURL = function(type)
{
	if( this.data.iconData[type] )
	{
		return this.data.iconData[type];
	}
	return null;
}

Template.prototype.getButton = function(type,id)
{
	switch( type )
	{
	case 'button':
		return this.buttons[id];
	case 'floorplan':
		return this.floorplans[id];
	case 'thumbnail':
		return this.thumbnails[id];
	}
	alert("unknown button type "+type);
}

Template.prototype.panoChanged = function( yaw ){
	if(this.floorplanDirection != null) {
		if(this.floorplanDirection != 'undefined') {
			var tmp = _jg;			
			if( _jg != null ){
				_jg.clear();
			}
			_jg = this.floorplanDirection;
			this.notifyYawChanged( yaw );
			//alert( (tmp == _jg) );
		}
	}
}

Template.prototype.notifyYawChanged = function( yaw )
{
	if(this.floorplanDirection != null) {
		if(this.floorplanDirection != 'undefined') {
			var page = this.brochure.getCurrentPage()
			//var currentViewable = page.currentViewableName;
			var currentViewable = page.viewables[page.currentViewableName];
			var newYaw=yaw;
			if(currentViewable.north) {
				if(currentViewable.north.yaw) {
					newYaw = newYaw + eval(currentViewable.north.yaw);
				}
			}
			if (newYaw>360) newYaw = newYaw - 360;
			if( _jg != null ){
				_jg.clear();
			}
			_jg = this.floorplanDirection;
			myDrawFunction(0,0,newYaw,40,this.floorplanDirection);
		}
	}
}

Template.prototype.notifyViewableChanged = function( id )
{
	this.brochure.getCurrentPage().currentViewableName = id;
	this.updateLayersDueToViewableChanged( id );
	this.resetButtonStates();
}

Template.prototype.updateLayersDueToViewableChanged = function(id)
{
	if( this.getLayers().Detailed_Image_Text )
	{
		this.brochure.reRenderLayer('Detailed_Image_Text',id);
	}
	if( this.getLayers().Thumbnails_Frame )
	{
		var params = new Array();
		params[0] = Brochure.getLayer( "Thumbnails_Frame" ).clientWidth;
		params[1] = id;
		this.brochure.reRenderLayer("Thumbnails_Frame",params);
	}
	if( this.getLayers().Floorplan )
	{
		this.brochure.reRenderLayer('Floorplan', id);
		if(this.floorplanDirection != null) {this.floorplanDirection.clear();this.floorplanDirection=null;}
		this.floorplanDirection = new jsGraphics("FloorplanDirection");
	}
	if( this.getLayers().Drop_Down_Menu )
	{
		this.brochure.reRenderLayer('Drop_Down_Menu', id);
	}
}

/**
 * Listens for buttons in the brochure and re-routes them to actions
 */
Template.prototype.buttonClicked = function( id )
{		//The definition of listeners comes from the data structure
	this.brochure.debug("Template.buttonClicked( "+id+" ) enter" );
	var acts = this.data.buttonData[id].actions;
	
	for( var i in acts )
	{
		this.brochure.debug("Template.buttonClicked( "+id+" ) action "+i+" "+acts[i] );
		var act = acts[i];		
		switch( typeof( act ) )
		{
		case "string":
			switch( acts[i] )
			{
			case "viewer":				
				this.brochure.debug("Template.buttonClicked( "+id+" ) calling viewer.doAction" );
				this.brochure.viewer.doAction( i );
				this.brochure.debug("Template.buttonClicked( "+id+" ) called viewer.doAction" );
				break;
			case "template":				
				if( typeof( this[i] ) == "function" )
				{
					this.brochure.debug("Template.buttonClicked( "+id+" ) calling function on self "+i+"()" );
					this[i]();
					this.brochure.debug("Template.buttonClicked( "+id+" ) called function on self "+i+"()" );
				}
				break;
			}
			break;
		case "function":
			act(i);
		}
	}
	this.brochure.debug("Template.buttonClicked( "+id+" ) exit" );
}

Template.prototype.getLayers = function()
{
	return this.data.layers;
}


////////////////FORMAT LAYER FUNCTIONS

/**
 * Formats and returns a string containing the HTML necessary to render the
 * client branding layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getClient_BrandingContent = function(flag,params)
{
	var a = this.brochure.client;
	var leftColumn = '';

	if (a.logoURL)
	{
		if( a.webAddress )
		{
			leftColumn = '<a href="' + a.webAddress + '" target="_new"><img src="' + a.logoURL + '" border="0" alt="' + a.webAddress + '"></a>';
		}
		else
		{
			leftColumn = '<img src="' + a.logoURL + '">';
		}
	}
	else
		leftColumn = '&nbsp;';

	var rightColumn = '';
	if (a.title)
		rightColumn += a.title;
	if (a.webAddress)
		rightColumn += '<br>Web: <a href="' + a.webAddress + '" target="_new">' + a.webAddress + '</a>';
	if (a.address)
		rightColumn += '<br>' + a.address;
	if (a.phone)
		rightColumn += '<br>Phone: ' + a.phone;
	if (a.emailAddress)
		rightColumn += '<br>Email: <a href="#" onclick="theBrochure.buttonClicked(\'button\',\'email\')">' + a.emailAddress + '</a>';
	if (this.brochure.getCurrentPage().clientname)
		rightColumn += '<br>Name: ' + this.brochure.getCurrentPage().clientname;
	if (this.brochure.getCurrentPage().clientphone)
		rightColumn += '<br>Contact: ' + this.brochure.getCurrentPage().clientphone;

	return '<table cellspacing="0" cellpadding="0">'
		+ '<tr>'
		+ '<td class="clientBrandingImage">' + leftColumn + '</td>'
		+ '<td class="Client_Branding">' + rightColumn + '</td>'
		+ '</tr>'
		+ '</table>';
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * portal branding layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getPortal_BrandingContent = function(flag,params)
{
	var p = this.brochure.portal;
	var leftColumn = '';

	if (p.logoURL)
	{
		if( p.webAddress )
		{
			leftColumn = '<a href="' + p.webAddress + '" target="_new"><img src="' + p.logoURL + '" border="0" alt="' + p.webAddress + '"></a>';
		}
		else
		{
			leftColumn = '<img src="' + p.logoURL + '">';
		}
	}
	else
	{
		leftColumn = '&nbsp;';
	}

	var rightColumn = '';
	if (p.title)
		rightColumn += p.title;
	if (p.webAddress)
		rightColumn += '<br><a href="' + p.webAddress + '" target="_new">' + p.webAddress + '</a>';
	if (p.phone)
		rightColumn += '<br>' + p.phone;
	if (p.emailAddress)
		rightColumn += '<br>Email: <a href="mailto:' + p.emailAddress + '">' + p.emailAddress + '</a>';

	return '<table cellspacing="0" cellpadding="0">'
		+ '<tr>'
		+ '<td class="portalBrandingImage">' + leftColumn + '</td>'
		+ '<td class="Portal_Branding">' + rightColumn + '</td>'
		+ '</tr>'
		+ '</table>';
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * page links layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getAction_ButtonsContent = function(flag,params)
{
	//Page Links
	var pageNames = this.brochure.getPageNames();
	var unorderedListHTML = '&nbsp;';

	if (pageNames.length)
	{
		var listItemsHTML = '';

		for (var i = 0; i < pageNames.length; i++)
		{
			var page = this.brochure.getPage(pageNames[i]);
			var pageTitle = '';

			if (page.title)
				pageTitle = page.title;
			else
				pageTitle = 'Page ' + (i + 1);

			listItemsHTML += '<div style="float:left;" class="Action_Buttons">';
			listItemsHTML += '<table cellpadding="0" cellspacing="0"><tr style="background-image:url(' + this.getUnformattedImage('genericButtonMid') +')">';
			listItemsHTML += '<td nowrap>' + this.getFormattedButton('genericButtonLeft') + '</td>'; //get left side of button
			listItemsHTML += '<td nowrap><a href="#"  class="Action_Buttons" onClick="theBrochure.changePage(\'' + pageNames[i] + '\');return false;">' + pageTitle + '</a></td>'; //get middle of button
			listItemsHTML += '<td nowrap>' + this.getFormattedButton('genericButtonRight') + '</td>'; //get end of button
			listItemsHTML += '</tr></table>';
			listItemsHTML += '</div>';
		}
		unorderedListHTML = listItemsHTML;
	}

	return unorderedListHTML;
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * Template buttons layer. The template buttons include an overview button,
 * zero or more floorplan buttons and zero or one guided tour buttons.
 *
 * @return A string containing HTML.
 */
Template.prototype.getTemplateButtonsLayerContent = function(flag,params)
{
	// TODO
	// The floorplan buttons need to be in their own layer because
	// some brochure will contain a very large number of floorplans
	// and the navigation between the floorplan will need to be via
	// the floorplans themselves and not via buttons. Putting the
	// floorplan buttons in their own layer will enable all of the
	// floorplan buttons to be made invisible in cases where the
	// number of floorplans is great and button control is not desired.

	// Format the overview button.
	var text = '<table cellpadding="0" cellspacing="0">'
		+ '<tr><td>' + this.getFormattedButton('overview') + '</td></tr>\n';

	// Append the floorplans buttons HTML to the result.
	text += this.formatFloorplanButtons('<tr><td>','</td></tr>');

	// Append the guided tour button to the result.
	var guidedTourText = this.getFormattedButton('templateGuidedTour');

	if (guidedTourText)
		text += '<tr><td>' + guidedTourText + '</td></tr>\n';

	text += '</table>\n';
	//return text;
	return ''; /// ABOVE CODE COMMENTED OUT BY DS 20060403 JP TO COMMENT ON BUG #930

}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * system buttons layer. The system buttons include the print and email
 * buttons.
 *
 * @return A string containing HTML.
 */
//Template.prototype.getFunctions_ButtonsContent = function(flag,params)
Template.prototype.formatFunctions_Buttons = function(divTag,flag,params)
{
	var str = '';
	// Format the overview button.
 	var layerName = "General_Text";
 	var layer = Brochure.getLayer(layerName);
 	if( layer != null )
 	{
	 	var overlappingLayerNames = this.brochure.getOverlappingLayerNames(layerName);
		if (overlappingLayerNames.length > 0) str += this.getFormattedButton('overview');
 	}

 	layerName = "Thumbnails_Frame";
 	layer = Brochure.getLayer(layerName);
 	if( layer != null )
 	{
	 	var overlappingLayerNames = this.brochure.getOverlappingLayerNames(layerName);
		if (overlappingLayerNames.length > 0) str += this.getFormattedButton('thumbnails');
 	}

	// Append the floorplans buttons HTML to the result.
 	layerName = "Floorplan";
 	layer = Brochure.getLayer(layerName);
 	if( layer != null )
 	{
	 	var overlappingLayerNames = this.brochure.getOverlappingLayerNames(layerName);
		if (overlappingLayerNames.length > 0) str += this.formatFloorplanButtons('','');
 	}

	// Append the guided tour button to the result.
	var guidedTourText = this.getFormattedButton('templateGuidedTour');
	var page = this.brochure.getCurrentPage();
	if(page.guidedTour.num > 0)
		if (guidedTourText)
				str += guidedTourText;

	str+=this.getFormattedButton('print');

 	layerName = "Email_Frame";
 	layer = Brochure.getLayer(layerName);
 	if( layer != null )
 	{
	 	var overlappingLayerNames = this.brochure.getOverlappingLayerNames(layerName);
		if (overlappingLayerNames.length > 0) str+=this.getFormattedButton('email');
 	}
	//return str;
	divTag.innerHTML="<p class='Functions_Buttons'>"+ str + "</p>";;

	//Deprecated 20060310
	//return '<table cellpadding="0" cellspacing="0">'
	//	+ '<tr><td>' + this.getFormattedButton('print') + '</td></tr>\n'
	//	+ '<tr><td>' + this.getFormattedButton('email') + '</td></tr>\n'
	//	+ '</table>\n';
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * heading layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getMain_Heading_TextContent = function(flag,params)
{
	return '<p class=\"Main_Heading_Text\">' + this.brochure.getCurrentPage().heading1 + '</p>';
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * subheading layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getSub_Heading_TextContent = function(flag,params)
{
	var page = this.brochure.getCurrentPage();
	return '<p class=\"Sub_Heading_Text\">' + page.heading2 + '</p>'
		+ '<p class=\"Sub_Heading_Text\">' + page.heading3 + '</p>';
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * general text layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getGeneral_TextContent = function(flag,params)
{
    var tempStr = this.brochure.getCurrentPage().description;
    tempStr = tempStr.replace(/\r/gi,'</p><p class="General_Text">');
	return '<p class="General_Text">' + tempStr + '</p>';
}

/**
 * This is called back by the formatting system to re-do the thumbnails if needed
 */
Template.prototype.modifyThumbnails = function( prevWidth )
{
	var innerWidth = Brochure.getLayer( "Thumbnails_Frame" ).clientWidth;
	if( innerWidth )
	{
		innerWidth=""+innerWidth;
		if( innerWidth.substring( innerWidth.length-2 ) == "px" )
		{
			innerWidth = innerWidth.substring( 0, innerWidth.length-2 );
		}
		innerWidth *= 1;
	}
	if( innerWidth < prevWidth )
	{
		this.brochure.debug( "Re rendering the thumbnails because innerWidth="+innerWidth+" prevWidth="+prevWidth);
		var params = new Array();
		params[0]=innerWidth;
		this.brochure.reRenderLayer("Thumbnails_Frame",innerWidth);
	}
}

/**
 * Formats the div tag to render the thumbnails layer.
 * Special care should be taken in this method to ensure
 * that the thumbnail rendering degrades gracefully in old browsers and the
 * Netscape browser. This was an issue in the old brochures, so care should
 * be taken not to repeat past mistakes.
 */
Template.prototype.formatThumbnails_Frame = function(divTag,flag,params)
{
	var currentViewableName = null;
	if(params!=null){ if(params[1]) currentViewableName = params[1];}
	var text = '';
	var innerWidth = -1;
	var width = Utility.getStyleFromTag(divTag, 'width', 'width');
	if( width.substring( width.length-2 ) == "px" )
	{
		width = width.substring( 0, width.length-2 );
	}
	width *= 1;
	if( !flag )
	{		//First time through - set a callback for when we know if we have a scrollbar
			//We use the full width less 16 for now
		innerWidth = width-16;
		this.addAfterFormatCallback( {onFormatFinished:function(){
			setTimeout("theBrochure.getTemplate().modifyThumbnails("+innerWidth+")");
		}} );
	}
	else
	{		//Callback - use the clientWidth property for the width to scale to
		innerWidth = params[0];
	}

		//A table to contain one cell for each line
	text += '<table width="'+innerWidth+'" cellspacing="0" cellpadding="0" border=0 align="left">';
	var span = 0;
		//A table for each line to contain one cell for each thumbnail
	text += '<tr align="left"><td align="left"><table cellspacing="0" cellpadding="5" border=0 align="left"><tr align="left">';
	var first = true;
	// a little fix to cope with orders not being in linear sequence, eg. 3,4,1 (no occurance of 2 at all is legal)
	var idMax = 0;
	for(var it in this.thumbnailsByOrder) {
		if(parseInt(it)>idMax) idMax=parseInt(it);
	}
	for(var id=0; id <= idMax; id++)
	{
		if(th = this.thumbnailsByOrder[id]) {
			span+=10+th.outerWidth;
			if( first )
			{
				first = false;
			}
			else if( span > innerWidth )
			{		//Go to next line before writing the next
				text+='</tr></table></td></tr>';
				text += '<tr align="left"><td align="left"><table cellspacing="0" cellpadding="5" border=0 align="left"><tr align="left">';
				span = 10+th.outerWidth;
			}
			if(currentViewableName == th.id) { // highlight currentViewable
				text += '<td valign=top align=left class=Thumbnails_Frame>'+th.format(true)+'</td>\n';
			}
			else {
				text += '<td valign=top align=left class=Thumbnails_Frame>'+th.format(false)+'</td>\n';
			}
		}
	}
	text+='</tr></table></td></tr></table>';
	if( flag )
	{		//When redoing, clear the div first
		Brochure.unloadDiv(divTag);
	}
	//divTag.style.overflow = "auto";
	text = "<DIV style=\"overflow:hidden;width:"+innerWidth+"px\">"+text+"</DIV>";
	divTag.innerHTML=text;
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * detailed image description layer.
 *
 * @return A string containing HTML.  Note that params is the id of the viewable
 */
Template.prototype.getDetailed_Image_TextContent = function(divTag,flag,params)
{
	if( params == null )
	{
		params = this.brochure.viewer.getCurrentViewable(false);
	}
	if( params == null )
	{
		return;
	}
	var currentViewableParams = this.brochure.getCurrentPage().viewables[params];
	var tempStr = currentViewableParams.description;
	if(currentViewableParams.Latitude) {
		tempStr = tempStr + '<p><br>Latitude : '+currentViewableParams.Latitude;
	}
	if(currentViewableParams.Longtitude) {
		tempStr = tempStr + '<br>Longtitude: '+currentViewableParams.Longtitude;
	}
	tempStr = tempStr.replace(/\r/gi,'</p><p class="Detailed_Image_Text">');
	return '<p class="Detailed_Image_Text">' + tempStr + '</p>';
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * floorplan layer.
 *
 * @return A string containing HTML.
 */
//Template.prototype.getFloorplanContent = function(flag,params)
Template.prototype.formatFloorplan = function(divTag,flag,params)
{
	var page = this.brochure.getCurrentPage();
	var floorplan = this.brochure.getCurrentFloorplan();
	if( floorplan != null )
	{
		var foundPanoHotspot=false;
		var directionCenterX=0;
		var directionCenterY=0;
		var text = '<div style="border:1px;position:absolute;'
			+ 'left:0px;'
			+ 'top:0px;'
			+ 'width:' + 100 + '%;'
			+ 'height:' + 100 + '%;">'
			+ '<div style="position: absolute;"><img src="' + floorplan.image + '"></div>';
		//alert('hello');
		for (var hotspot in floorplan.HotspotsArray)
		{
			var pointsArray;
			if(hotspot != "listType") {
				var targetName = floorplan.HotspotsArray[hotspot].targetName;
				var page = this.brochure.getCurrentPage();
				var type = null;
				if( typeof( page.viewables[targetName] ) != "undefined" )
				{
					type = page.viewables[targetName].type;
				}
				else if( typeof( page.floorplans[targetName] ) != "undefined" )
				{
					type = 'floorplan';
				}
				var points = floorplan.HotspotsArray[hotspot].points;
				pointsArray =  points.split(',');

				var iconURL	= this.getFloorplanIconURL(type);
				if (iconURL)
				{
					text+=
						'<div style="position:absolute;'
						+ 'left:' + pointsArray[0] + 'px;'
						+ 'top:' + pointsArray[1] + 'px;';
						//+ 'width:' + pointsArray[2] + 'px;'
						//+ 'height:' + pointsArray[3] + 'px;">'
						text+= 'width:' + 60 + 'px;'
						+ 'height:' + 60 + 'px;">'
						+ '<img src="' + iconURL + '" '
						+ 'alt="' + page.viewables[targetName].short_desc + '" '
						+ 'onMouseOver="javascript:window.status=\''+ page.viewables[targetName].short_desc +'\'" '
						+ 'onClick="theBrochure.floorPlanActionCallback(\'' + hotspot + '\')" '
						+ 'style="cursor:hand;';
						if (targetName==params) {
							text+= 'border:1px solid #FF0000;';
						}
						text+='" '
						+ '>' //+ '<font class="Floorplan">'+ page.viewables[targetName].short_desc + '</font>'
						+ '</div>\n';
				}
				// get points to center floorplanDirection feature
				if(targetName==params) {
					directionCenterX = eval(pointsArray[0]) +9; // add 9 to compensate for hotspot icons sizes
					directionCenterY = eval(pointsArray[1]) +9; // add 9 to compensate for hotspot icons sizes
					foundPanoHotspot=true;
				}
			}
		}
		if(foundPanoHotspot) {
			text += '<div id="FloorplanDirection" style="left:' + directionCenterX + 'px;top:' + directionCenterY + 'px;WIDTH:100%;POSITION:absolute;HEIGHT:100%"></div>';
		}
		text += '</div>';
		divTag.innerHTML = text;
	}
	return "";
}

/**
 * Checks to see if floorplan layer is ontop (NOT USED CURRENTLY)
 */
Template.prototype.showFloorplanCheck = function(id)
{
	var onTop = this.brochure.isLayerOnTop("Floorplan");
	alert('hello');
	if( onTop )
	{
		this.brochure.debug( "Re rendering the floorplan layer");
		this.showFloorplan(this.brochure.getCurrentPage().currentViewableName);
	}
}

/**
 * Returns the HTML necessary to format the floorplans surrounding each floorplan with HTML in prefix and suffix.
 * TODO ensure order is good.
 */
Template.prototype.formatFloorplanButtons = function(prefix,suffix)
{
	var text = "";
	for (var id in this.floorplans)
	{
		text += prefix+this.floorplans[id].format()+suffix;
	}
	return text;
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * help layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getHelp_FrameContent = function(flag,params)
{		//TODO
	//return "HELP HERE";


/*
		+ '<td'+toolBarBack+'>' + this.getFormattedImage('leftEnd') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.formatRemoteToolbarImage() + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('mute') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('zoomOut') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('zoomIn') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('help') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('left') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('stop') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('right') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('enlarge') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedButton('toolbarGuidedTour') + '</td>\n'
		+ '<td'+toolBarBack+'>' + this.getFormattedImage('rightEnd') + '</td>\n'

		*/
return	"<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">"
  + "	  <tr>"
  + "	    <td colspan=\"2\" class=Help_Frame><strong>Viewer Help</strong></td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>"+this.getFormattedButton('zoomIn')+"</td>"
  + "	    <td class=Help_Frame>This button zooms in or tightens the viewing angle of a panorama so as to be able to see aspects of a scene from a closer perspective.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>"+ this.getFormattedButton('help') +"</td>"
  + "	    <td class=Help_Frame>This button calls the help information you are now reading.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>"+ this.getFormattedButton('left') +"</td>"
  + "	    <td class=Help_Frame>This button pans the panorama to the left.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>" + this.getFormattedButton('stop') + "</td>"
  + "	    <td class=Help_Frame>This button pauses any movement within the viewer screen.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>" + this.getFormattedButton('right') + "</td>"
  + "	    <td class=Help_Frame>This button pans the panorama to the right.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>" + this.getFormattedButton('mute') + "</td>"
  + "	    <td class=Help_Frame>This button turns sound ON or OFF.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>" + this.getFormattedButton('toolbarGuidedTour') + "</td>"
  + "	    <td class=Help_Frame>This button begins the guided tour sequence which displays all aspects of the tour in turn.</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td valign=\"top\" class=Help_Frame>NOTE:</td>"
  + "	  </tr>"
  + "	  <tr>"
  + "	    <td colspan=\"2\" class=Help_Frame>Some of these features are unavailable when viewing a still image.</td>"
  + "	  </tr>"
  + "</table>";
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * email layer.
 *
 * @return A string containing HTML.
 */

Template.prototype.getEmail_FrameContent = function(flag,params)
{
	var page = this.brochure.getCurrentPage();

	return '<form name="eMailData" id="eMailData">'
		+ '<table border="0" cellpadding="1" cellspacing="0">'
		+ '<tr><td class=Email_Frame><strong>Tell a friend or ask the client about this tour</strong></tr>'
		+ '<tr><td class=Email_Frame>To (email)</td></tr>'
		+ '<tr><td class=Email_Frame><input type="text" name="toEmailAddress" size="20" value="' + this.brochure.client.emailAddress + '"></td></tr>'
		+ '<tr><td class=Email_Frame>From (email)</td></tr>'
		+ '<tr><td class=Email_Frame><input type="text" name="fromEmailAddress" size="20"></td></tr>'
		+ '<tr><td class=Email_Frame>Your Name</td></tr>'
		+ '<tr><td class=Email_Frame><input type="text" name="sender" size="20"></td></tr>'
		+ '<tr><td class=Email_Frame>Subject</td></tr>'
		+ '<tr><td class=Email_Frame><input type="text" name="subject" size="20" value="' + page.title + '"></td></tr>'
		+ '<tr><td class=Email_Frame>Message</td></tr>'
		+ '<tr><td>'
		+ '<textarea cols="20" rows="6" name="message">'
		+ page.heading1 + '\n'
		+ page.heading2 + '\n'
		+ page.heading3 + '\n'
		+ '</textarea>'
		+ '</td></tr>'
		+ '</form>'
		+ '<tr><td class=Email_Frame>'
		+ '<form name="mailForm" action="" method="post" enctype="text/plain" __onSubmit="Template.sendMail();">'		
		+ '<input type="submit" onClick="Template.sendMail();" value="Send">'
		+ '</td></tr>'
		+ '</form>'
		+ '</table>';
}


/**
 * This is called to set up the initial viewer layer content by returning the mark-up as a string.
 * This method is deleted by itself after first usage so that the formatViewer is used subsequently.
 */

Template.prototype.getInitialViewerLayerContent = function(flag,params)
{
	return ">"+this.brochure.getViewer().getViewerLayerContent();
}

/**
 * Formats the viewer layer to take into consideration the new situation.
 */
Template.prototype.formatViewerLayer = function(divTag,flag,params)
{
	this.brochure.getViewer().formatViewerLayer( divTag, flag, params );
}

Template.prototype.unloadViewerLayer = function()
{
	this.brochure.getViewer().unloadViewerDiv();
}

/**
 * Formats and returns a string containing the HTML necessary to render the
 * viewer toolbar layer.
 *
 * @return A string containing HTML.
 */
Template.prototype.getView_Controls_FrameContent = function(flag,params)
{
	var toolBarBack = ' style="background-image:url(' + this.getUnformattedImage('toolBarBackground') + ');" ';
	var str = '';
	str+= '<table width="100%" cellpadding="0" cellspacing="0"><tr>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedImage('leftEnd') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.formatRemoteToolbarImage() + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('mute') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('zoomOut') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('zoomIn') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('help') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('left') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('stop') + '</td>\n';
	str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('right') + '</td>\n';
	//str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('enlarge') + '</td>\n';
	var page = this.brochure.getCurrentPage();
	if(page.guidedTour.num > 0)
		str+=  '<td'+toolBarBack+'>' + this.getFormattedButton('toolbarGuidedTour') + '</td>\n';
	str+=  '<td'+toolBarBack+' align=right>' + this.getFormattedImage('rightEnd') + '</td>\n';
	str+=  '</tr></table>\n';
	return str;
}

/**
 * The toolbar has an extra image which changes depending on what button you are hovering over.
 * Format that button here
 */
Template.prototype.formatRemoteToolbarImage = function()
{
	return this.data.remoteToolbar.format(this.data.remoteToolbar);
}

/**
 * Private class method used by formatEmail_Frame() to send an email message.
 */
Template.sendMail = function()
{

    var body =  'Page Title: ' + document.title + '%0A'
        	+ 'Mailed From: ' + document.location + '%0A'
        	+ 'Sender Name: ' + document.eMailData.sender.value + '%0A%0A'
    		+ document.eMailData.message.value;
    	
    document.mailForm.action =
    	'mailto:' + document.eMailData.toEmailAddress.value + '?subject=' + document.eMailData.subject.value
    	+ '&body=' + body;    
    
    document.mailForm.submit();
}


////////////showXYZLayer functions

/**
 * Shows the help layer.
 */
Template.prototype.showHelp_Frame = function()
{
	this.brochure.showLayer('Help_Frame');
}

/**
 * Shows the thumbnails frame.
 */
Template.prototype.showThumbnails_Frame = function()
{
	this.brochure.showLayer('Thumbnails_Frame');
}

/**
 * Shows the email layer.
 */
Template.prototype.showEmail_Frame = function()
{
	this.brochure.showLayer('Email_Frame');
}

/**
 * Shows the generalText layer.
 */
Template.prototype.showGeneral_Text = function()
{
	this.brochure.showLayer('General_Text');
}

/**
 * Shows the print dialog layer.
 */
Template.prototype.showPrintDialog = function()
{
	var da = (document.all) ? 1 : 0;
	var pr = (window.print) ? 1 : 0;
	var mac = (navigator.userAgent.indexOf("Mac") != -1);

	if (pr) { // NS4, IE5
	    window.print();
	} else if (da && !mac) { // IE4 (Windows)
	    vbPrintPage();
	} else { // other browsers
	    alert("Sorry, your browser doesn't support this feature.");
	}

	if (da && !pr && !mac) with (document) {
	  writeln('<OBJECT ID="WB" WIDTH="0" HEIGHT="0" CLASSID="clsid:8856F961-340A-11D0-A96B-00C04FD705A2"></OBJECT>');
	  writeln('<' + 'SCRIPT LANGUAGE="VBScript">');
	  writeln('Sub window_onunload');
	  writeln('  On Error Resume Next');
	  writeln('  Set WB = nothing');
	  writeln('End Sub');
	  writeln('Sub vbPrintPage');
	  writeln('  OLECMDID_PRINT = 6');
	  writeln('  OLECMDEXECOPT_DONTPROMPTUSER = 2');
	  writeln('  OLECMDEXECOPT_PROMPTUSER = 1');
	  writeln('  On Error Resume Next');
	  writeln('  WB.ExecWB OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER');
	  writeln('End Sub');
	  writeln('<' + '/SCRIPT>');
	}
}

Template.prototype.enlargeViewer = function()
{		//TODO
	alert("Enlarge Viewer");
}
Template.prototype.formatDebugLayer = function( debugLayer, flag, params )
{
	if( flag )
	{
		Brochure.unloadDiv(divTag);
	}
	debugLayer.style.top="600px";
	var text = "";
	if( this.brochure.debugText )
	{
		text = this.brochure.debugText;
	}
	debugLayer.innerHTML='<TEXTAREA id="debugTA" cols="80" rows="20">'+text+'</TEXTAREA>';
}

Template.prototype.getViewer_FrameContent = function(flag,params)
{
	return "";
}

Template.prototype.getExternal_LinksContent = function(flag,params)
{
	return "";
}

Template.prototype.getDrop_Down_MenuContent = function(flag,params)
{
	var page = this.brochure.getCurrentPage();
	var currentViewable = null;
	if (params) currentViewable=params;
	var text = '';

	var idMax = 0;
	for(var it in this.thumbnailsByOrder) {
		if(it>idMax) idMax=it;
	}
	if(idMax > 0)
	{

			//A table for each line to contain one cell for each thumbnail
			text += '<select name="mydropdown" onChange="theBrochure.buttonClicked(\'thumbnail\',this.value)">';
			var optionText = "";
			// a little fix to cope with orders not being in linear sequence, eg. 3,4,1 (no occurance of 2 at all is legal)

			for(var id=0; id <= idMax; id++)
			{
				if(th = this.thumbnailsByOrder[id]) {
					optionText = page.viewables[th.id].short_desc;
					if(optionText == "") optionText=th.id;
					text += '<option ';
					if(currentViewable == th.id) text += 'selected="true" ';
					text += 'value="'+th.id+'">'+optionText+'</option>\n';
				}
			}
			text+='</select>';
	}
	return text;

}

Template.prototype.getUser_Defined_Frame_1Content = function(flag,params)
{
// User defined arbitary html code
//	var htmlCode = '';
//	htmlCode += '<img src="test.jpg">';
//	return htmlCode;
//
	return "";
}

Template.prototype.getUser_Defined_Frame_2Content = function(flag,params)
{
	return "";
}

Template.prototype.getUser_Defined_Frame_3Content = function(flag,params)
{
	return "";
}

Template.prototype.getUser_Defined_Frame_4Content = function(flag,params)
{
	return "";
}

Template.prototype.getUser_Defined_Frame_5Content = function(flag,params)
{
	return "";
}

Template.prototype.getUser_Defined_Frame_6Content = function(flag,params)
{
	return "";
}

Template.prototype.getUser_Defined_Frame_7Content = function(flag,params)
{
	return "";
}

Template.prototype.getUser_Defined_Frame_8Content = function(flag,params)
{
	return "";
}

