// NevLab.js
//
// Copyright © 2009 Neville Campbell.  All rights reserved.
// NevLab Theater™ is a trademark of Neville Campbell
//
// An (X)HTML page that uses Silverlight should have a JavaScript section that follows
// the pattern shown below:
//
//		<script type="text/javascript">
//			window.NevLab = {
//				usesSilverlight: true,
//				screenshot: "Quicksort350.png"
//			};
//
//			if (!window.Silverlight) {
//				window.Silverlight = {};
//			}
//			Silverlight.disableAutoStartup = true;      
//		</script>
//		<script src="Silverlight.js" type="text/javascript" />
//		<script src="Silverlight.supportedUserAgent.js" type="text/javascript" />
//		<script src="NevLab.js" type="text/javascript" />
//
// The window.NevLab declaration can optionally define some of the following properties:
//
//		usesSilverlight					// Boolean
//		screenshot						// e.g. "Quicksort350.png"
//		safari3Compatible				// Boolean
//		operaCompatible					// Boolean
//		debug							// Boolean
//
//	Each page should use NevLab.js even if Silverlight is not used, however the rest of
//	the JavaScript section above may be safely omitted.  While debugging, a window.NevLab
//	object should be defined, and NevLab.debug should be set to true.
//	NevLab.usesSilverlight should either be omitted or set to true.
//
//	The page banner should be immediately followed by.
//
//			<div id="promptHost"></div>
//			<div class="noJavaScript">
//				<noscript>
//					<p>
//						This website uses JavaScript, which is currently disabled in your
//						browser.  Please enable it so you can view this website properly.
//					</p>
//				</noscript>
//			</div>
//
//	The Silverlight control host div should be in the form shown below and followed by
//	the specified script.
//
//			<div id="silverlightControlHost" >
//				<object	id="silverlightControl" type="application/x-silverlight" ... >
//					<param ... />
//						...
//					<param name="autoUpgrade" value="false" />
//					<param name="onSourceDownloadProgressChanged" value="onSourceDownloadProgressChanged" />
//					<param name="onLoad" value="onSilverlightLoad" />
//					<param name="onError" value="onSilverlightError" />
//
//					<div id="silverlightNotInstalled">
//					</div>
//				</object>
//				<iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
//			</div>
//			<script type="text/javascript">
//				NevLab.silverlightUpgradeFirefoxKludge();
//			</script>
//

if (!window.Silverlight) {
	window.Silverlight = {};
}

if (!window.NevLab) {
	window.NevLab = {};
}

NevLab.showMessage = function(html) {
	// If I inject upgrade prompt html in the silverlightControlHost as per Microsoft
	// whitepaper, Internet Explorer sometimes experiences a fatal error and must
	// shut down.  Furthermore I want notification at the top of screen.
	document.getElementById("promptHost").innerHTML = html;

	// Avoid confusion in silverlightControlHost.  e.g. Silverlight is ignoring
	// autoUpgrade = false, so Silverlight's upgrade prompt is being displayed
	// within the silverlightControlHost.
	var silverlightControlHost = document.getElementById("silverlightControlHost");
	silverlightControlHost.style.visibility = "hidden";
	silverlightControlHost.style.marginBottom = "10px";
	document.getElementById("silverlightControl").style.height = 0;
	window.scrollTo(0, 0);
}

// messageLines is array of strings
NevLab.showAlert = function(messageLines) {
	var html = "<div class='text silverlightAlert'>";
	var i;
	for (i = 0; i < messageLines.length; i++) {
		html += "<p>" + messageLines[i] + "</p>";
	}
	html += "</div>";
	NevLab.showMessage(html);
}

NevLab.errorAnnouncement = "Something went wrong!";
NevLab.reloadPrompt = "Try reloading the page.";

NevLab.PromptToReload = function() {
	var textLines = [NevLab.errorAnnouncement, NevLab.reloadPrompt];
	NevLab.showAlert(textLines);
}

NevLab.showDebug = function (preformattedText) {
	var html = "" +
		"<div class='text debug'><pre>" +
			NevLab.errorAnnouncement + " " + NevLab.reloadPrompt + "\n\n" +
			preformattedText +
		"</pre></div>";

	NevLab.showMessage(html);
}

window.onerror = function(msg, url, line) {
	if (NevLab.debug) {

		var text = "" +
			"Error:  " + msg + "\n\n" +
			"URL:    " + url + "\n\n" +
			"Line:   " + line;

		NevLab.showDebug(text);
	}
	else {
		NevLab.PromptToReload();
	}
	return true;
}

// for script debugging
NevLab.Trace = function(message) {
	if (!NevLab.Trace.text) {
		NevLab.Trace.text = "\nTrace:\n";
	}
	NevLab.Trace.text += message + "\n";
}

// adapted from docs for Silverlight web page integration
NevLab.showSilverlightError = function(sender, args) {
	var appSource = "";
	if (sender != null && sender != 0) {
		appSource = sender.getHost().Source;
	}

	var errorType = args.ErrorType;
	var iErrorCode = args.ErrorCode;

	var errMsg = "Unhandled Error in Silverlight Application " + appSource + "\n";

	errMsg += "Code:  " + iErrorCode + "\n";
	errMsg += "Category:  " + errorType + "\n";
	errMsg += "Message:  " + args.ErrorMessage + "\n";

	if (errorType == "ParserError") {
		errMsg += "XamlFile:  " + args.xamlFile + "\n";
		errMsg += "Line:  " + args.lineNumber + "\n";
		errMsg += "Position:  " + args.charPosition + "\n";
	}
	else if (errorType == "RuntimeError") {
		if (args.lineNumber != 0) {
			errMsg += "Line: " + args.lineNumber + "\n";
			errMsg += "Position: " + args.charPosition + "\n";
		}
		errMsg += "MethodName: " + args.methodName + "\n";
	}
	if (NevLab.Trace.text) {
		errMsg += NevLab.Trace.text;
	}
	NevLab.showDebug(errMsg);
}

// Silverlight would not find function in NevLab, so it's global

onSilverlightError = function(sender, args) {
	NevLab.checkSilverlightSupport();
	if (!NevLab.silverlightSupport) {
		return;
	}
	else if (args.ErrorCode == 8001) {
		Silverlight.onUpgradeRequired();
		Silverlight.__installationEventFired = true;
	}
	else if (Silverlight.IsVersionAvailableOnError(sender, args)) {
		if (NevLab.debug == true) {
			NevLab.showSilverlightError(sender, args);
		}
		// Silverlight app wants to reload page
		else if (args.ErrorMessage.indexOf("Page Reload Requested") >= 0) {
			location.reload();
		}
		else {
			NevLab.PromptToReload();
		}
	}
}

// Silverlight would not find function in NevLab, so it's global
onSourceDownloadProgressChanged = function (sender, eventArgs) {
	var progress = Math.round(eventArgs.progress * 100);
	sender.findName("Status").Text = "Downloading: " + progress + "%";
	sender.findName("ProgressBar").ScaleY = eventArgs.Progress * 356;
}

// Silverlight would not find function in NevLab, so it's global
onSilverlightLoad = function(sender, args) {
	NevLab.checkSilverlightSupport();
	if (!NevLab.silverlightSupport) {
		return;
	}
	Silverlight.IsVersionAvailableOnLoad(sender);
}

Silverlight.onRequiredVersionAvailable = function() {
};

Silverlight.onRestartRequired = function () {
	var msg = ["Please restart your browser and return to this page."];
	NevLab.showAlert(msg);
};

NevLab.installSilverlight = function () {
	window.location = "http://go2.microsoft.com/fwlink/?linkid=149156";
}

NevLab.description = "<p>NevLab Theater shows interactive demos of " +
	"algorithms and other programming topics.</p>";

if(!NevLab.screenshot) {
	NevLab.screenshot = "unknown.png";
}

NevLab.screenshotTag = "<div><img class='sampleScreenshot' src='";
NevLab.screenshotTag += NevLab.screenshot;
NevLab.screenshotTag += "' alt='Screenshot' /></div>";

// and not too ancient!
NevLab.isWindows = window.navigator.userAgent.indexOf('Windows NT') >= 0;
NevLab.isPreVistaWindows = window.navigator.userAgent.indexOf('Windows NT 5') >= 0;

// Boolean updating -- true if updating, false if installing for first time.
NevLab.inviteSilverlightInstallation = function(updating) {
	var reason = updating ? "This demo require an updated version of Microsoft Silverlight." :
		"Microsoft's Silverlight player is required to run this demo.";

	var html = "" +
		"<div id='installSilverlight' class='invitation'>" +
			NevLab.screenshotTag +
			"<div class='invitationContent'>" +
				NevLab.description +
				"<p>" +
					reason + "  " +
					"Please click on the Silverlight icon below, " +
					"and take a few seconds to install this small, unobtrusive plugin";
	if (NevLab.isPreVistaWindows) {
		html += "*";
	}
	html += "." +
				"</p>" +
				"<p>" +
					"<img src='http://go.microsoft.com/fwlink/?LinkId=108181'";
	html += updating ?	"onclick='NevLab.UpgradeClicked();'" : "onclick='NevLab.InstallClicked();'";
	html +=				"alt='Get Microsoft Silverlight'" +
						"id='installSilverlightImg' />" +
				"</p>";
	if (NevLab.isPreVistaWindows) {
		html += "<p>" +
					"*<a href='http://silverlight.net/forums/t/89383.aspx'>" +
						"Windows XP users may need to be logged on with administrative " +
						"privileges.</a>" +
				"</p>";
	}
	html += "</div>" +
		"</div>";

	NevLab.showMessage(html);
}

Silverlight.onInstallRequired = function() {
	NevLab.inviteSilverlightInstallation(false);
}

Silverlight.onUpgradeRequired = function() {
	if (!Silverlight.onUpgradeRequired.Invoked) {
		NevLab.inviteSilverlightInstallation(true);
		Silverlight.onUpgradeRequired.Invoked = true;
	}
}

// action -- "installing" or "updating"
NevLab.SilverlightClicked = function(action) {
	NevLab.installSilverlight();

	var messageLines = ["You may need to reload this page or restart your browser after " +
		action + " Silverlight."];

	NevLab.showAlert(messageLines);
}

NevLab.InstallClicked = function () {
	NevLab.SilverlightClicked("installing");
}

NevLab.UpgradeClicked = function () {	
	NevLab.SilverlightClicked("updating");
}

// Returns the maximum major verion of IE advertised by the user agent string,
// or 0 if no such version is reported.
NevLab.MaxIEMajorVersion = function() {
	var ua = window.navigator.userAgent;
	var regex = /MSIE (\d+\d?)/g;
	var max = 0;
	var a, n;
	while (a = regex.exec(ua)) {
		n = parseInt(a[1], 10);
		if (n > max) {
			max = n;
		}
	}
	return max;
}

NevLab.checkWebCompatibility = function() {
	var ieMajorVersion = NevLab.MaxIEMajorVersion();
	if (ieMajorVersion == 0 || ieMajorVersion >= 8) {
		return;
	}
	if (ieMajorVersion == 7) {
		var ie7Html = "<div class='text debug'><p>" +
		"You appear to be using Internet Explorer 7, which has some ugly " +
		"incompatibilities with this website. An upgrade to the " +
				"<a href='http://www.google.com/search?q=download+internet+explorer'>" +
				"latest version</a> is strongly recommended." +
		"</p></div>";
		document.getElementById("promptHost").innerHTML = ie7Html;
		return;
	}
	var html = "" +
		"<div>" +
			"<h3>" +
				"You appear to be using an old version of Internet Explorer" +
				" that is not supported by this website." +
			"</h3><h3>" +
				"An upgrade to the " +
				"<a href='http://www.google.com/search?q=download+internet+explorer'>" +
				"latest version</a> is strongly recommended." +
			"</h3>" +
		"</div>";

	document.write(html);
	if (ieMajorVersion < 7) {
		NevLab.runOnLoad.stop = true;
	}
}

NevLab.isIntelMac = window.navigator.userAgent.indexOf('Intel Mac OS X') >= 0;

NevLab.isPowerPCMac = function() {
	return window.navigator.userAgent.indexOf('PPC Mac') >= 0;
}

NevLab.isCompatibleSystem = NevLab.isWindows || NevLab.isIntelMac;

NevLab.isChrome = window.navigator.userAgent.indexOf('Chrome') >= 0;

// Chrome 2.0.172.33 seems compatible with Silverlight 3; stable channel auto updates?
NevLab.isCompatibleChrome = function() {
	return NevLab.isCompatibleSystem && NevLab.isChrome;
}

// Chrome user agent string contains "Safari"
NevLab.isSafari = window.navigator.userAgent.indexOf('Safari') >= 0 && !NevLab.isChrome;

// version 4 seems OK
NevLab.isCompatibleSafari = function() {
	if (!NevLab.isSafari || !NevLab.isCompatibleSystem) {
		return false;
	}
	var ua = window.navigator.userAgent;
	if (ua.indexOf('Version/1') >= 0 || ua.indexOf('Version/2') >= 0) {
		return false;
	}
	return ua.indexOf('Version/3') < 0 || NevLab.safari3Compatible;
}

// not sure about Intel Mac version; ignore version issues;
NevLab.isCompatibleSeaMonkey = function() {
	var ua = window.navigator.userAgent;
	return NevLab.isCompatibleSystem && ua.indexOf('SeaMonkey') >= 0;
}

// Temporary?
NevLab.isCompatibleOpera = function() {
	if (window.navigator.userAgent.indexOf('Opera') < 0) {
		return false;
	}
	var operaHtml = "<div class='text debug'><p>" +
		"You appear to be using the Opera web browser, which has some " +
		"<a href='http://www.nevlab.com/blog/2009/10/20/a-night-at-the-opera/'>" +
		"incompatibilities</a> with the Silverlight applications on this website." +
		"</p></div>";
	document.getElementById("promptHost").innerHTML = operaHtml;
	return true;
}

//	// ignore version issues;
//	NevLab.isCompatibleOpera = function() {
//		var ua = window.navigator.userAgent;
//		return NevLab.isCompatibleSystem && NevLab.operaCompatible && ua.indexOf('Opera') >= 0;
//	}

// may not matter
NevLab.isSearchEngineSpider = function() {
	var ua = window.navigator.userAgent;
	return ua.indexOf('Googlebot') >= 0 || ua.indexOf('msnbot') >= 0 ||
		ua.indexOf('Yahoo! Slurp') >= 0;
}

// Silverlight versions 2 and 3 support same OS/Browser combinations.
// As of July 10, 2009, Silverlight.supportedUserAgent.js only accepts version
// values of "1.0" and "2.0".
NevLab.isSupportedUserAgent = function() {
	return Silverlight.supportedUserAgent("2.0") ||
		NevLab.isCompatibleChrome() || NevLab.isCompatibleSafari() ||
		NevLab.isCompatibleSeaMonkey() || NevLab.isCompatibleOpera() ||
		NevLab.isSearchEngineSpider();
}

// Perform check at most once

NevLab.checkSilverlightSupport = function() {
	if (NevLab.silverlightCheckSupportStarted) {
		return;
	}
	NevLab.silverlightCheckSupportStarted = true;
	NevLab.silverlightSupport = NevLab.isSupportedUserAgent();
	if (NevLab.silverlightSupport) {
		return;
	}
	var invitation;
	if (NevLab.isSafari && !NevLab.isCompatibleSafari() && !NevLab.isPowerPCMac()) {
		invitation = "" +
			"You appear to be using an old version of the Safari browser that is " +
			"unsupported by this website because of a " +
			"<a href='PluginResizeTest.htm'>Silverlight incompatibility</a>.  " +
			"An upgrade to the <a href='http://www.google.com/search?q=download+Safari'>" +
			"latest version</a> is strongly recommended.";
	}
	else {
		var unsupportedSystem = NevLab.isPowerPCMac() ? "PowerPC Macs such as yours" :
			"your combination of web browser and operating system";

		invitation = "" +
			"This demo uses Microsoft Silverlight, which does not " +
			"support " + unsupportedSystem + ".  " +
			"Please take a moment to review this website's " +
			"<a href='SystemRequirements.htm'>system requirements</a>."
	}
	var html = "" +
		"<div class='invitation'>" +
			"<div>" +
				"<img class='sampleScreenshot' src='" + NevLab.screenshot + "' " +
					 "alt='Screenshot' />" +
			"</div>" +
			"<div class='invitationContent'>" +
				NevLab.description +
				"<p>" +
					invitation +
				"</p>" +
			"</div>" +
		"</div>";

	NevLab.showMessage(html);
	NevLab.runOnLoad.stop = true;
}

// for Firefox users upgrading Silverlight from 1.0 or beta of version 2
// see Silverlight Installation Experience Whitepaper

NevLab.silverlightUpgradeFirefoxKludge = function() {
	try {
		if (navigator.plugins["Silverlight Plug-In"].description) {
			var html = "" +
				"<div class='text silverlightAlert'>" +
					"<p>" +
						"Please restart your browser and return to this page."
					"</p>";
				"</div>";

			document.getElementById("silverlightNotInstalled").innerHTML = html;
		}
	}
	catch (e) {
		// not Firefox or Silverlight not installed
	}
}

// start runOnLoad -- see Flanagan 5th Edition, page 434

NevLab.runOnLoad = function(f) {
	if (NevLab.runOnLoad.stop) {
		return;
	}
	else if (NevLab.runOnLoad.loaded) {
		f();
	}
	else {
		NevLab.runOnLoad.funcs.push(f);
	}
}

NevLab.runOnLoad.funcs = [];
NevLab.runOnLoad.loaded = false;

NevLab.runOnLoad.run = function() {
	if (NevLab.runOnLoad.loaded) {
		return;
	}
	for (var i = 0; i < NevLab.runOnLoad.funcs.length; i++) {
		try {
			NevLab.runOnLoad.funcs[i]();
			if (NevLab.runOnLoad.stop) {
				break;
			}
		}
		catch (e) {
		}
	}
	NevLab.runOnLoad.loaded = true;
	delete NevLab.runOnLoad.funcs;
	delete NevLab.runOnLoad.run;
}

if (window.addEventListener) {
	window.addEventListener("load", NevLab.runOnLoad.run, false);
}
else if (window.attachEvent) {
	window.attachEvent("onload", NevLab.runOnLoad.run);
}
else {
	window.onload = NevLab.runOnLoad.run;
}

// check web compatibility first!
NevLab.runOnLoad(NevLab.checkWebCompatibility);

if (NevLab.usesSilverlight) {
	NevLab.runOnLoad(NevLab.checkSilverlightSupport);
	NevLab.runOnLoad(Silverlight.__startup);
}

// end runOnLoad

