//
// HDView2.js    copyright 2009, Microsoft Corporation
//
// This script contains helper functions for initializing the HD View
// control.  Currently HD View is supported on IE, Firefox, Flock, Netscape,
// and Safari under Windows.
// For many users you should be able to just directly link to this 
// script on the HD View web site:
//   <script type="text/javascript" src="http://research.microsoft.com/en-us/um/redmond/groups/IVM/HDView/HDView2.js"></script>
// There are several advantages to this, including that bug fixes can
// be made in one common location and that if the control version changes
// this script will prompt users that an update is available.
// 
// For users who need to add functionality, you may modify this script and 
// use it on your own site.
// 
function hdvHelperFn()
{
	// variables that determine the platform
	var ua   = navigator.userAgent;
	var isIE = ( ua.indexOf( "MSIE" ) != -1 );
	var isSupportedBrowser = ( isIE ||
		ua.indexOf( "Firefox" ) != -1 ||  // also true for Flock, Netscape
		ua.indexOf( "Safari" ) != -1 );
	var isSupportedOS = ( ua.indexOf("Windows NT 5.1") != -1 || 
		ua.indexOf("Windows NT 5.2") != -1 ||
		ua.indexOf("Windows NT 6") != -1 );
	var isSupportedSilverlightBrowser = (isSupportedBrowser ||
		ua.indexOf("Opera") != -1 );	// not officially supported, but it works
	var isSilverlightSupportedOS = (ua.indexOf("Windows") != -1 ||
		ua.indexOf("Intel Mac OS X") != -1 );

	// number of names generated for global event handlers
	var handlerCount = 0;
	
	// public functions
	this.getMimeType = function() { return "application/x-hdview";} 
	this.getClsid    = function() { return "F81FB289-0FB6-4FE0-A488-101447EE1ED3"; }
	this.getCodepath = function() { return "http://research.microsoft.com/en-us/um/redmond/groups/IVM/HDView/"; }
	this.getSLpath   = function() { return "http://research.microsoft.com/en-us/um/redmond/groups/IVM/HDViewSL/"; }
	this.getVersion  = function() { return "3,3,0,0";  }
	this.getSilverlightVersion = function() { return "2.0.31005.0"; }
	this.isOldIE = function () { return isIE && ua.indexOf("MSIE 9") == -1; }
	this.isOSSupported = function() { return isSupportedOS; }
	this.isPlatformSupported = function() { return isSupportedOS && isSupportedBrowser; }
	this.isSilverlightOSSupported = function() { return isSilverlightSupportedOS; }
	this.isSilverlightPlatformSupported = function() { return isSilverlightSupportedOS && isSupportedSilverlightBrowser; }
	this.isHDViewInstalled = function()
	{
		// Returns null if HD View is not installed at all,
		// false if an older version is installed, or true if
		// the required version (or newer) is installed.
		var installed = null;
		try {
			var reqVersionArray = this.getVersion().split(',');
			var pluginVersionArray;
			if (isIE) {
				var hdvCntrl = new ActiveXObject("HDView.HDViewControl");
				if (hdvCntrl) {
					pluginVersionArray = hdvCntrl.Version.split('.');
				}
			}
			else {
				navigator.plugins.refresh(false);
				var hdvPlugin = navigator.plugins["HD View"];
				if (hdvPlugin) {
					var versionMatch = /\d+\.\d+\.\d+\.\d+/.exec(hdvPlugin.description);
					if (versionMatch) {
						pluginVersionArray = versionMatch[0].split('.');
					}
				}
			}
			installed = pluginVersionArray ?
				(reqVersionArray[0] < pluginVersionArray[0] ||
				(reqVersionArray[0] == pluginVersionArray[0] &&
				 reqVersionArray[1] <= pluginVersionArray[1])) : null;
		}
		catch (e) { }
		return installed;
	}
	this.isSilverlightInstalled = function()
	{
		// Returns null if Silverlight is not installed at all,
		// false if an older version is installed, or true if
		// the required version (or newer) is installed.
		var installed = null;
		var div = null;
		try {
			var plugin = null;
			if (isIE) plugin = new ActiveXObject("AgControl.AgControl");
			else {
				navigator.plugins.refresh(false);
				if (navigator.plugins["Silverlight Plug-In"]) {
					div = document.createElement("div");
					document.body.appendChild(div);
					div.innerHTML = '<embed type="application/x-silverlight" />';
					plugin = div.childNodes[0];
				}
			}
			installed = plugin ? (plugin.IsVersionSupported(this.getSilverlightVersion()) ? true : false) : null;
			plugin = null;
		}
		catch (e) { }
		if (div) document.body.removeChild(div);
		return installed;
	}
	this.addWindowEvent = function(eventName, func) {
		var oldHandler = window[eventName];
		if (typeof oldHandler != "function") {
			window[eventName] = func;
		}
		else {
			window[eventName] = function() {
				oldHandler();
				func();
			}
		}
	}
	this.generateHandlerName = function(handler) {
		var handlerName = null;
		if (typeof handler == "string") {
			handlerName = handler;
		}
		else if (typeof handler == "function") {
			if (handlerCount == 0) {
				this.addWindowEvent("onunload", cleanupGeneratedHandlers);
			}
			var count = handlerCount++;
			handlerName = "__hdvEvent" + count;
			window[handlerName] = handler;
		}
		return handlerName;
	}
	
	// private functions
	function cleanupGeneratedHandlers() {
		for (var i = handlerCount - 1; i >= 0; i--) {
			window["__hdvEvent" + i] = null;
		}
		handlerCount = 0;
	}
};
if (!window.hdvHelper) {
	window.hdvHelper = new hdvHelperFn();
}

function hdvHost(id, elementId, width, height, href, args, allowAbsoluteUrl)
{	
	// public functions
	this.setArgs             = setArgs;
	this.setProperty         = setProperty;
	this.getProperty         = getProperty;
	this.permalinkPopUp      = permalinkPopUp;
	this.getPermalink        = getPermalink;
	this.getEmbed            = getEmbed;

	// store the constructor arguments
	var argId        = id;
	var argElementId = elementId;
	var argWidth     = width;
	var argHeight    = height;
	var argHref      = href; 
	var argArgs      = args;
	var argAllowAbsoluteUrl = allowAbsoluteUrl;

	var parent = null;
	var hdvControl = null;
	var propertyHolder = null;
	var deferredProperties = {};
	var currentFilename = "";
	var currentConversion = "";
	var currentViewer = 0; // 0 = HDView only, 1 = DeepZoom only, 2 = Dual

	//
	// the "constructor"
	//
	// synopsis: If this is not a supported platform this function writes
	//           some html into the page to that effect.  Otherwise it 
	//           first detects if the control is installed on this machine.
	//           If it is not installed then some html is written into the
	//           page that offers users a link to the install page.  If it is 
	//           installed, then the control will be embedded in the supplied 
	//           'elementId'.
	// 

	constructor();

	function constructor()
	{
		setArgs(argArgs);
		parent = document.getElementById(argElementId);

		if (currentConversion != "") {
			parent.innerHTML = buildHTMLMessage('<img src="http://research.microsoft.com/en-us/um/redmond/groups/ivm/HDView/Preview/loading.gif" width="16" height="16" alt=""/>' +
				'<div style="padding: 10px 0 0 0;">Converting...</div>');
			var callback = hdvHelper.generateHandlerName(conversionCallback);
			setTimeout(function()
			{
				loadScript("http://hdviewconvert.cloudapp.net/Convert.aspx?InputUrl=" + encodeURIComponent(currentConversion) + "&Callback=" + callback);
			}, 0);
		}
		else {
			createViewer();
		}
	}

	// other functions
	function createViewer()
	{
		if (currentViewer == 1 || (currentViewer == 2 && !hdvHelper.isPlatformSupported())) {
			// Use Silverlight.
			if (!hdvHelper.isSilverlightPlatformSupported()) {
				parent.innerHTML = buildHTMLForSilverlightNotSupported();
			}
			else if (!hdvHelper.isSilverlightInstalled()) {
				parent.innerHTML = buildHTMLForSilverlightInstall();
			}
			else {
				parent.innerHTML = buildHTMLForSilverlight(argId, argWidth, argHeight);
				hdvControl = document.getElementById(argId);
			}
		}
		else {
			// Use HD View.
			if (!hdvHelper.isPlatformSupported()) {
				parent.innerHTML = buildHTMLForNotSupported();
			}
			else if (!hdvHelper.isHDViewInstalled()) {
				parent.innerHTML = buildHTMLForInstall();
			}
			else {
				parent.innerHTML = buildHTMLForControl(argId, argWidth, argHeight);
				hdvControl = document.getElementById(argId);
				propertyHolder = hdvControl;
				setDeferredProperties();
			}
		}
	}

	function setArgs(argString)
	{
		var args = argString.split('&');
		for (var i = 0; i < args.length; i++) {
			var arg = args[i];
			var index = arg.indexOf("=");
			var prop = arg.substr(0, index);
			var val = arg.substr(index + 1);
			if (prop.length > 0 && val.length > 0) {
				if (prop == "Viewer") {
					if (!hdvControl) {
						currentViewer = val;
					}
				}
				else if (prop == "Convert") {
					if (!hdvControl) {
						currentConversion = unescape(val);
					}
				}
				else {
					setProperty(prop, val);
				}
			}
		}
	}

	function setProperty(prop, val)
	{
		// If we don't yet have a target to set the property on,
		// just remember the property and value for later.
		if (!propertyHolder) {
			deferredProperties[prop] = val;
		}
		else {
			if( prop == "FileName" ) {
				val = unescape(val);
				currentFilename = val;
				if (val.charAt(0) == '#')
				{
					val = val.substring(1, val.length);
					val = document.getElementById(val).text;
				}
				else
				{
					if ( !argAllowAbsoluteUrl )
					{
						var pageArgs = argHref.split('?');
						var pagePath = pageArgs[0].substring(0, pageArgs[0].lastIndexOf('/') + 1);
						val = pagePath + val;
					}
				}
			}

			// The result of a conversion is set using the XML property.
			// We send it to the viewer using the FileName property,
			// but don't store it in currentFilename.
			if (prop == "XML") {
				prop = "FileName";
				currentFilename = "";
			}

			// Determine local decimal point representation and format
			// the val correctly under IE, Firefox uses "." regardless.
			if( prop == "FOV" || prop == "Yaw" || prop == "Pitch" ||
				prop == "Zoom" || prop == "XCtr" || prop == "YCtr" )
			{
				val = val.toString(); // allow a number or a string
				var separator = Number(1.1).toLocaleString().charAt(1);
				val = val.replace('/\\' + separator + '/', '.');
			}

			if( prop == "BackgroundColor" || prop ==  "ForegroundColor" )
				val = parseInt(val);

			propertyHolder[prop] = val;
		}
	}

	function getProperty(prop)
	{
		if( propertyHolder ) {
			return propertyHolder[prop];
		}
	}

	//
	// function: permalinkPopUp
	//
	// synopsis: call this function to create a pop-up window that contains
	//           a 'permalink' url for the current control view parameters.  
	//           The user is prompted to copy the url to the clipboard.
	//
	function permalinkPopUp()
	{
		if (hdvControl) {
			var permalink = getPermalink();
			permalink = permalink.replace(/&/g, '&amp;'); // encode for HTML
			var embed = getEmbed();
			embed = embed.replace(/&/g, '&amp;'); // encode for HTML

			win = window.open("", "", "width=450, height=180");
			win.document.open();
			win.document.write('<html><head><title>HD View</title></head><body>');
			win.document.write('<div style="font: 12px/18px Verdana, Arial, Helvetica, sans-serif; text-align: center;">');
			win.document.write('<form id=LinkForm name=LinkForm>');
			win.document.write('Link to paste in email or IM:<br/><input type="text" id="PermaLink" size="50" value="' +
				permalink + '" contentEditable="false" onclick="select()"/><br/><br/>');
			win.document.write('HTML to embed in web site:<br/><input type="text" id="IFrame" size="50" value="' +
				embed + '" contentEditable="false" onclick="select()"/><br/><br/>');
			win.document.write('</form>');
			win.document.write('</div></body></html>');
			win.document.close();
		}
	}

	function getPermalink()
	{
		var pageArgs = argHref.split('?');
		var permafile = "FileName=" + escape(currentFilename);
		if (currentConversion != "") {
			permafile = "Convert=" + escape(currentConversion);
		}
		var permalinkargs = getPermalinkArgs();
		return pageArgs[0] + '?' + permafile + permalinkargs;
	}

	function getEmbed()
	{
		var pageArgs = argHref.split('?');
		var pagePath = pageArgs[0].substring(0, pageArgs[0].lastIndexOf('/') + 1);
		var embedfile = "FileName=" + (!argAllowAbsoluteUrl ? pagePath : "") + escape(currentFilename);

		if (currentConversion != "") {
			embedfile = "Convert=" + escape(currentConversion);
		}
		else if (currentFilename.charAt(0) == '#') {
			var embedxml = document.getElementById(currentFilename.substring(1, currentFilename.length));
			// convert tabs to spaces for spaces.live.com
			var xmltext = embedxml.text.replace(/\t/g, ' ').replace(/\s+/g, ' ');
			if (hdvHelper.isOldIE())
				embedxml = embedxml.XMLDocument;
			else
				embedxml = new DOMParser().parseFromString(xmltext.substr(xmltext.indexOf("?>") + 2), "text/xml");

			var imageset = embedxml.getElementsByTagName("imageset");
			if (imageset.length > 0) {
				// need to fix up relative path if any
				imageset = imageset[0].attributes;

				// check for relative path (not url or root)
				var embedurl = imageset.getNamedItem("url");
				if (embedurl.value.indexOf("https://") < 0 &&
						embedurl.value.indexOf("http://") < 0 &&
						embedurl.value.indexOf("file://") < 0 &&
						embedurl.value.indexOf("ftp://") < 0 &&
						embedurl.value.charAt(0) != '/' &&
						embedurl.value.charAt(0) != '\\' &&
						embedurl.value.charAt(1) != ':') {
					embedurl.value = pagePath + embedurl.value;

					xmltext = '<?xml version="1.0"?>\r\n<root>\r\n  <imageset\r\n';
					for (var i = 0; i < imageset.length; i++)
						xmltext += '    ' + imageset[i].name + '=\"' + imageset[i].value + '\"\r\n';
					xmltext += '  />\r\n</root>\r\n';
				}
			}

			embedfile = "FileName=" + escape(xmltext);
		}

		var permalinkargs = getPermalinkArgs();
		permalinkargs = permalinkargs.replace(/&/g, '&amp;'); // encode for HTML

		var iframe = "<iframe style='width: 400px; height: 300px; margin: 0' frameborder='0' scrolling='no' src='" +
				"http://research.microsoft.com/en-us/um/redmond/groups/ivm/HDViewSL/HDIFrame.htm?" + embedfile + permalinkargs + "'></iframe>";
		return iframe;
	}

	function getPermalinkArgs()
	{
		var permalinkargs;
		var fov = getProperty('FOV');
		if (fov > 0)
			permalinkargs = "&FOV=" + Math.round(fov * 1000) / 1000 +
							"&Yaw=" + Math.round(getProperty('Yaw') * 1000) / 1000 +
							"&Pitch=" + Math.round(getProperty('Pitch') * 1000) / 1000;
		else
			permalinkargs = "&Zoom=" + Math.round(getProperty('Zoom') * 1000) / 1000 +
							"&XCtr=" + Math.round(getProperty('XCtr') * 1000000) / 1000000 +
							"&YCtr=" + Math.round(getProperty('YCtr') * 1000000) / 1000000;
		var panMode = getProperty("PanMode");
		if (panMode != 0)
			permalinkargs += "&PanMode=" + panMode;
		var toneMode = getProperty("ToneMode");
		if (toneMode != 0)
			permalinkargs += "&ToneMode=" + toneMode;
		var projMode = getProperty("ProjMode");
		if (projMode != 0)
			permalinkargs += "&ProjMode=" + projMode;
		var backgroundColor = getProperty("BackgroundColor");
		if (backgroundColor != 0x000000)
			permalinkargs += "&BackgroundColor=0x" + backgroundColor.toString(16);
		var foregroundColor = getProperty("ForegroundColor");
		if (foregroundColor < 0)
			foregroundColor += 0x100000000;  // work around bug in older HD View versions
		if (foregroundColor != 0xFFFFFF00)
			permalinkargs += "&ForegroundColor=0x" + foregroundColor.toString(16);
		if (currentViewer != 0)
			permalinkargs += "&Viewer=" + currentViewer;
		return permalinkargs;
	}

	function buildHTMLForInstall()
	{
		var reqVersionArray = hdvHelper.getVersion().split(',');
		var installPage = hdvHelper.getCodepath() + 'HDInstall.htm';
		var installImage = hdvHelper.getCodepath() + 'images/HDInstall3.jpg';
		var verb = hdvHelper.isHDViewInstalled() == null ? 'Install' : 'Upgrade to';
		return buildHTMLMessage(verb + ' HD View Beta ' + reqVersionArray[0] + '.' + reqVersionArray[1] + ' to see this content.<br/>' +
			'<a href="http://research.microsoft.com/en-us/um/redmond/groups/ivm/HDView/" target="_blank" style="color: #08D;">What is HD View?</a><br/><br/>' +
			'<a href="' + installPage + '" target="_blank" style="text-decoration: none;">' +
			'<img src="' + installImage + '" alt="Get HD View" style="border-style: none"/></a><br/><br/>' +
			'After installing, you may need to restart your browser.');
	}

	function buildHTMLForSilverlightInstall()
	{
		var verb = hdvHelper.isSilverlightInstalled() == null ? 'Install' : 'Upgrade to';
		return buildHTMLMessage(verb + ' the latest version of Silverlight to see this content.<br/>' +
			'<a href="http://www.microsoft.com/silverlight/overview/default.aspx" target="_blank" style="color: #08D;">What is Silverlight?</a><br/><br/>' +
			'<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">' +
			'<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/></a><br/><br/>' +
			'After installing, you may need to restart your browser.');
	}

	function buildHTMLForControl(id, width, height)
	{
		var html = "";
		html += '<object id="' + id + '"';
		html += ' type="' + hdvHelper.getMimeType() + '"';
		html += ' width="' + width + '"';
		html += ' height="' + height + '"';
		html += '></object>';

		return html;
	}

	function buildHTMLForSilverlight(id, width, height)
	{
		// Generate a unique global function name for the Silverlight plugin loaded handler for this instance.
		var handlerName = hdvHelper.generateHandlerName(silverlightPluginLoaded);

		var html = '';
		html += '<object id="' + id + '"';
		html += ' data="data:application/x-silverlight," type="application/x-silverlight-2" width="' + width + '" height="' + height + '">';
		html += '<param name="minRuntimeVersion" value="' + hdvHelper.getSilverlightVersion() + '"/>';
		html += '<param name="autoUpgrade" value="true"/>';
		html += '<param name="enableHtmlAccess" value="true"/>';
		html += '<param name="source" value="HDViewSL.xap"/>';
		html += '<param name="background" value="black"/>';
		html += '<param name="initParams" value="InitializationCompleted=' + handlerName + '"/>';
		html += buildHTMLMessage('This content requires Silverlight (version 2 or newer).<br/>' +
			'<a href="http://www.microsoft.com/silverlight/overview/default.aspx" target="_blank" style="color: #08D;">What is Silverlight?</a><br/><br/>' +
			'<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">' +
			'<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/></a><br/><br/>' +
			'After installing, you may need to restart your browser.');
		html += '</object>';

		return html;
	}

	function buildHTMLForNotSupported()
	{
		var html = '';
		if (!hdvHelper.isOSSupported()) {
			html += 'Sorry, but HD View is not supported on this operating system.<br /><br />' +
				'HD View works on Windows XP, Server 2003, and Vista.';
		}
		else {
			html += 'Sorry, but HD View is not supported on this browser.<br /><br />' +
				'HD View works on Internet Explorer, Firefox, Flock, Netscape, and Safari.';
		}
		html += '<br/><br/>' +
			'<a href="http://research.microsoft.com/en-us/um/redmond/groups/ivm/HDView/" target="_blank" style="color: #08D;">What is HD View?</a>';
		return buildHTMLMessage(html);
	}

	function buildHTMLForSilverlightNotSupported()
	{
		var html = '';
		if (!hdvHelper.isSilverlightOSSupported()) {
			html += 'Sorry, but Silverlight is not supported on this operating system.<br /><br />' +
				'Silverlight works on Windows and on Mac OS (Intel only).';
		}
		else {
			html += 'Sorry, but Silverlight is not supported on this browser.<br /><br />' +
				'Silverlight works on Internet Explorer, Firefox, Flock, Netscape, Opera, and Safari.';
		}
		html += '<br/><br/>' +
			'<a href="http://www.microsoft.com/silverlight/overview/default.aspx" target="_blank" style="color: #08D;">What is Silverlight?</a>';
		return buildHTMLMessage(html);
	}

	function buildHTMLMessage(message)
	{
		return '<table cellpadding="0" cellspacing="0" style="width: ' + argWidth + '; height: ' + argHeight + '; margin: 0; padding: 0; background-color: #000; color: #FFF; font: 12px/18px Verdana, Arial, Helvetica, sans-serif;">' +
			'<tr><td style="text-align: center; vertical-align: middle; margin: 0; padding: 10px;">' + message + '</td></tr></table>';
	}

	function silverlightPluginLoaded()
	{
		try {
			propertyHolder = hdvControl.content.settings;
			setDeferredProperties();
		}
		catch (exception) {
		}
	}

	function setDeferredProperties()
	{
		// Set XML before any other properties so that view changes are applied properly.
		if (deferredProperties.XML) {
			setProperty("XML", deferredProperties.XML);
			delete deferredProperties["XML"];
		}
		for (var prop in deferredProperties) {
			setProperty(prop, deferredProperties[prop]);
		}
		deferredProperties = {};
	}

	function loadScript(source)
	{
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src = source;

		var head = document.getElementsByTagName("head")[0] || document.documentElement;
		head.insertBefore(script, head.firstChild);
	}

	function conversionCallback(result)
	{
		if (result.ErrorMessage) {
			parent.innerHTML = buildHTMLMessage(result.ErrorMessage);
		}
		else {
			setProperty("XML", result.OutputText);
			createViewer();
		}
	}
}

