// PluginDetect v 0.2
// Nick Crossland - plugindetect@panoptics.co.uk
// www.panoptics.co.uk/go/plugindetect

/*

PluginDetect is a simple, flexible, object-oriented, unobstrusive Javascript library for detecting installed browser plugins, which are required to view VR panoramas. It is aimed at panorama producers and publishers.

USAGE
This script is free to download and use - if you make any changes or improvements you are encouraged to submit them back to us so we may incorporate them in the script. A credit or link back is appreciated.

(c) Panoptics Immersive Media Ltd.

UPDATES SINCE 0.1
- Add more plugin types
- Update detection code so a plugin may have more than one activeX name
- Change the way Java is detected


*/


function getPluginTypes() {
	// A [prototype] hash of all the plugin types we can detect.
	// e.g.
	// pluginName: ["mozName", ["activeXName1", "activeXName2"]]
	// Java is treated differently - it just needs a mozName of "java"
	var PluginDetectPluginTypes = $H({ 
		java: ["java", ""], // Java is a special case
		flash: ["Flash", "ShockwaveFlash.ShockwaveFlash.1"],
		director: ["Director", ["SWCtl.SWCtl.1", "SWCtl.SWCtl.10.1.1"]],
		quicktime: ["QuickTime", "QuickTimeCheckObject.QuickTimeCheck.1"],
		glpanoview: ["GLPanoView", "GLPanoViewAX.ActiveFormGLPanoView"],
		deval: ["DevalVR", "DevalVRXCtrl.DevalVRXCtrl.1"]
						});
	return PluginDetectPluginTypes;
}





// We are using Firebug to debug in Firefox. In IE this will cause errors
// unless we have an alternative. So create a dummy object using the same methods
// which stores the console messages (not doing anything with them yet)
if (typeof(console)!=undefined) {
	console = new AltConsole(); 
}
function AltConsole() {
	contents = [];
	this.log = function(str) { contents.push(str); }
	this.info = function(str) {	contents.push(str); }
}





// Class for the PluginDetect (main controller)
function PluginDetect(arg) {
	
	
	// Register all the plugin types
	var pluginTypes = getPluginTypes(); // 
	this.detectionDone = false;
	
	// pdb = plugin database, a hash of plugin objects. Empty at the moment until some plugin objects are added
	var pdb = $H([]);
	
	// If we only want to do some, we are supplied with a list of plugins in an arguement
	var p = arg.pluginTypes;

	// If no plugintypes are specified, we should detect them all
	if (!p) {
		console.log('No plugins specified, check for them all');
		p = [];
		pluginTypes.each(function (plg) {
								   p.push(plg.key);
								   }
						);
	}
	
	
	// Loop over each arguements supplied and turn them back on again
	var i = 0;	
	
	p.each(function (plg) {				
					// Look to see if this plugin is one we know about
					var thisPluginData = pluginTypes[plg];
					if (thisPluginData && !pdb[plg]) { // Does this plugintype exist? // And not already added
						console.log('adding '+ plg);
						pdb[plg] = (new PluginType(thisPluginData[0], thisPluginData[1]));
					} else { 
						// Remove this from the list if requested plugintypes does not exist
						delete(p[i]);
						console.warn(plg+' does not exist'); 
					}
					i++;
					 });
	p = p.compact(); // Use Prototype compact function to remove all the undefined ones we have just removed
		
	// End of constructor
	
	
	
	
	
	

	// Method for debugging only - show the detect status for each plugintype
	// i.e. are we going to look for it
	this.inspectPdb = function () {
		//console.log('The plugin database now looks like this: '+pdb.inspect());
		console.log('------------------------------------------------------');
		console.log('[inspectPdb] The plugin database now looks like this:');
		pdb.each(function(p) {
					var v = $H(p).value;	
					var k = $H(p).key;
					console.debug(k);
								 });
		console.log('------------------------------------------------------');
	}
	
	
	// Do the detection
	// Loop over each plugin type for those we have specified to act upon, in the order given
	this.detect= function () {
		pdb.each(function(plg) {   
			var po = plg.value;	// Plugin object 
			po.detect(); // Do the detection
			po.doActions();	 // Do the actions for this detection
		});
		this.detectionDone = true;
		
		return true;
	}
	
	// Method to check if detectoin has been done
	this.detectionComplete = function() {
		return this.detectionDone;	
	}
	
	// A method to return an array of all installed plugins
	// Based on the order supplied
	this.allInstalledPlugins = function() {
		if (!this.detectionDone) { return false; } // Make sure we have done the detection
		var installedPlugins = []; // A new array to return the installed plugins in 
		pdb.each(function (plg) { // Go over each requested plugin type which we have created an object for
				// Does this plugintype exist, and is it installed?
				if (plg.value.isInstalled()) { 
					installedPlugins.push(plg.key);
				}
		 });
		return installedPlugins;
	}




	this.inspectActions = function() {
		pdb.each(function (plg) { // Go over each requested plugin type which we have created an object for
				// Does this plugintype exist, and is it installed?
				var installed = plg.value.actions['installed'];
				var notInstalled = plg.value.actions['notInstalled'];
				
				installed.each(function (i) {
									console.log(plg.key + ' actions (installed): '+i.action+' ('+i.type+')');	 
										 });
				notInstalled.each(function (i) {
									console.log(plg.key + ' actions (notInstalled): '+i.action+' ('+i.type+')');	 
										 });
				//console.log(plg.key + ' actions (notInstalled): '+notInstalled.inspect());
		 });
	}


	// A method to tell each plugintype what to do when it is found or not found
	this.addAction = function(pluginType, state, theAction, theActionType) {
		
		// What state are we adding the action for?
		// Make sure it is a valid keyword, or return false
		if (state == 'installed') {
			var theState = 'installed';
		} else if (state == 'notInstalled') {
			var theState = 'notInstalled';
		} else { return false; }
		
		// What is the action type?
		// Make sure it is a valid keyword, or return false
		if (theActionType == 'url') {
			var type = 'url';
		} else if (theActionType == 'redirect') {
			var type = 'redirect';	
		} else if (theActionType == 'js') {
			var type = 'js';
		} else { var type = 'auto'; }

		// Make sure we have a valid plugin type, and if so, add the action to the appropriate state
		if (pdb[pluginType]) { // Does this plugintype exist?
				pdb[pluginType].actions[theState].push({action: theAction, type: type});
		} else { return false; } 
		
		return true; // If we have succeeded.
	}
}







// Class for PluginType objects
function PluginType(mozName, activeXName) {
	
	// Make activeXName an array, even if a string is supplied
	console.log ('Creating new plugin type for '+activeXName);
	if (!isArray(activeXName)) { var axn = [activeXName]; } else { var axn = activeXName; }
	
	// Constructor
	this.mozName = mozName; // Mozilla's name for the plugin the the Navigator.plugins array
	this.activeXName = axn; // IE's activeX name for the plugin. Could be an array.
	this.hasBeenFound = null; // Has it been found yet? Null initially, then true or false once detection has taken place
	

	// What to do by default
	// We're making actions an object in case there are more actions in the future, 
	// and to keep track of them all more easily
	this.actions = { installed:  [], notInstalled: [] }; 	

	// Do the detection
	this.detect = function() {
		
		// Is it installed?
		var installed = false;
		
		// Java is a special case
		if (mozName == 'java') {
			if (navigator.javaEnabled()) { installed = true; } else { installed = false }
		} else {
	
				// If Netscape/Mozilla style, check for the mozName within the navigator array
				if (navigator.plugins && navigator.plugins.length) {
					for (var i=0; i < navigator.plugins.length; i++ ) {
						var plugin = navigator.plugins[i];
						var pluginName = plugin.name.toLowerCase();
							if (pluginName.indexOf(mozName.toLowerCase()) > -1) { installed = true;	}
					}
				} else { // Otherwise if we are in IE, try and create an ActiveX object
					var qtObj = false;
					
					axn.each(function(activeXName) {
												try {
													qtObj = new ActiveXObject(activeXName);
													if (qtObj) { installed = true; } 
													//qtObj.Quit(); // tidy up
												}
												catch(e) { 
													installed = false;
												} 
									  });
					console.log('Detecting: ' + activeXName + ' - result is: ' + installed);
				}
		}
		
		// Return the correct result
		if (installed) {
			this.hasBeenFound = true;
			return true;
		} else { 
			this.hasBeenFound = false;
			return false; 
		}
	}
	
	
	// Method to see if this plugin has been found
	// Returns null if detection has not taken place yet, then true or false
	this.isInstalled = function() {
		return this.hasBeenFound;
	}	
	
	

	
	
	this.doActions = function() {		
	
		//console.log(this.hasBeenFound);
	
		// Make sure the detectiom has taken place already for this plugin
		if (this.hasBeenFound === null) { return false; }
		
		// If the plugin has been found
		if (this.hasBeenFound === true) { 
			var actionsToDo = this.actions['installed'];
		}
		
		// If the plugin has not been found
		if (this.hasBeenFound === false) { 
			var actionsToDo = this.actions['notInstalled'];
		}
		
		
		// Make sure there are some actions to do
		if (actionsToDo.length < 1) { return; }
		
		// Go through each action and do it
		actionsToDo.each(function (theAction) {
							var a = theAction.action;
							var t = theAction.type;
							console.info('Doing action: '+a);
							// If it looks like a URL, or we have specified it is
							if ((isUrl(a) && t!='js') || t == 'redirect') {
								location.replace(a);
							} else if (t == 'url') {
								location.href = a;
							} else if (t == 'stop') {
								// Stop doing ANY more detections after this
								// TO DO
								return;
							} else {
							// Otherwise it must be js, so eval it
								eval(a);
							}
							   });
		
	}
	
}





// Function to see if a variable is an array
function isArray(arr) {
	//console.log('array to be tested is: '+arr);
	if(arr.constructor == Array ) {
		return true
	} else { return false; }
}

// Function to check if a string looks like a www URL (i.e. begins with http://)
function isUrl(url) {
	if (typeof(url) == 'string' && url.indexOf('http://') === 0) {
		return true;
	} else { return false; }
}

