var PluginFactory = function() {
	this.isInstalled = function(name) {
	return Plugin.getInfo(name).isInstalled;
}

this.getVersion = function(name) {
	return Plugin.getInfo(name).version;
}

this.getPluginsForMimeType = function(mimeType) {
	var result = [];
	if (supportsNavigatorPlugins()) {
		for (var i=0; i<navigator.mimeTypes.length; i++) {
			if (navigator.mimeTypes[i].type.indexOf(mimeType) == 0 && navigator.mimeTypes[i].enabledPlugin) {
				var pluginName = (findPluginName(navigator.mimeTypes[i].enabledPlugin.name) || navigator.mimeTypes[i].enabledPlugin.name);
				if (!Array.contains(result, pluginName)) result.push(pluginName);
			}
		}
	} else {
	// Code for IE using ActiveX
		for (var pluginName in Plugin.PLUGINS) {
			var mimeTypes = Plugin.PLUGINS[pluginName].acceptedMimeTypes;
			if (!mimeTypes) continue;
			for (var j=0; j<mimeTypes.length; j++) {
				if (mimeTypes[j].type.indexOf(mimeType) == 0 && Plugin.isInstalled(pluginName)) {
					if (!Array.contains(result, pluginName)) result.push(pluginName);
				}
			}
		}    
	}
	return result;
}

this.getPluginsForFileSuffix = function(suffix) {
	var result = [];
	if (supportsNavigatorPlugins()) {
		for (var i=0; i<navigator.mimeTypes.length; i++) {
			if ((","+navigator.mimeTypes[i].suffixes+",").indexOf(","+suffix+",") != -1 && navigator.mimeTypes[i].enabledPlugin) {
				var pluginName = (findPluginName(navigator.mimeTypes[i].enabledPlugin.name) || navigator.mimeTypes[i].enabledPlugin.name);
				if (!Array.contains(result, pluginName)) result.push(pluginName);
			}
		}
	} else {
		for (var pluginName in Plugin.PLUGINS) {
			var mimeTypes = Plugin.PLUGINS[pluginName].acceptedMimeTypes;
			if (!mimeTypes) continue;
			for (var j=0; j<mimeTypes.length; j++) {
				if ((","+mimeTypes[j].suffixes+",").indexOf(","+suffix+",") != -1 && Plugin.isInstalled(pluginName)) {
					if (!Array.contains(result, pluginName)) result.push(pluginName);
				}
			}
		}    
	}
	return result;
}

this.getInfo = function(name) {
	var info = Plugin.PLUGINS[name];
	var isInstalled = false;
	var version = null;
	if (supportsNavigatorPlugins()) {
		var plugin = findNavigatorPluginByName((name == "RealPlayer") ? "RealPlayer Version Plugin" : name);
		if (plugin) {
			isInstalled = true;
			version = getVersionFromPlugin(plugin);
		}
	} else {
		isInstalled = hasActiveXObject(Plugin.PLUGINS[name] && Plugin.PLUGINS[name].progID);
		if (isInstalled) {
			if (Plugin.PLUGINS[name].getActiveXVersionInfo) {
				version = Plugin.PLUGINS[name].getActiveXVersionInfo();
			} else {
				var progID = getProgIdForActiveXObject(Plugin.PLUGINS[name].progID);
				version = getVersionFromPlugin(progID);
			}
		} else {
			version = getActiveXPluginByClassId(Plugin.PLUGINS[name] && Plugin.PLUGINS[name].classID);
			if (version) version = version.replace(/,/g, ".");
			isInstalled = (version!=undefined);
		}
	}

	var result = {};
	for (var i in info) {
		result[i] = info[i];
	}
	result["isInstalled"] = isInstalled;
	result["version"] = version;
	result["name"] = name;
	return result;
}

/**
 * writes an embed or object tag to document.write or target.
 * @param plugin   name of the plugin to be used
 * @param options  options for embed respectivly object tag.
 *   .src,.width,.height,.type,.activeXType will get a special treatment
 *   all other properties of options will be added to the 
 *   embed tag as attributes resp. to the object tag as param(eters).
 *   option names should be lower case!
 * @param target   optional (id of) container element for the embed/object tag
 */
this.embed = function(plugin, options, target) {
	options = options || {};

	var embedOptions = Object.extend({}, options);
	var src = embedOptions.src;
	delete embedOptions.src;
	var id = embedOptions.id;
	delete embedOptions.id;
	var name = embedOptions.name || id;
	delete embedOptions.name;
	var width = embedOptions.width;
	delete embedOptions.width;
	var height = embedOptions.height;
	delete embedOptions.height;
	var type = embedOptions.type || (Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].mimeType) || "";
	delete embedOptions.type;
	var activeXType = embedOptions.activeXType || (Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].activeXType) || type;
	delete embedOptions.activeXType;
	var forceEmbedTag = (Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].forceEmbedTag === true) ? true : false;
	var forceObjectTag = (Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].forceObjectdTag === true) ? true : false;

	var embedOptions = Object.extend(((Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].standardEmbedAttributes) || {}), embedOptions);

	switch (plugin) {
		case "QuickTime":
		// get space for controls
		if (embedOptions.controller == "true" && (height+"").indexOf("%") == -1) {
			height += 16;
		}
		if (!options.activeXType) {
			activeXType = null;
		}
		break;

		case "Flash":
		// flash wants the src to be named "movie" if passed as object param
		if (!supportsNavigatorPlugins()) {
			embedOptions.movie = src;
			src = null;
		}
		break;

		case "RealPlayer":
		break;        

		default: 
		// do nothing
		break;
	}

	// prepare html code
	var html = "";
	if ((supportsNavigatorPlugins() && ! forceObjectTag) || forceEmbedTag) {
		// Netscape Plugin embed Tag
		html += '<embed' + getAttributeHtml("src", src)  + getAttributeHtml("id", id) + getAttributeHtml("name", name) + getAttributeHtml("width", width) + getAttributeHtml("height", height) + getAttributeHtml("pluginspage", Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].pluginsPage) + getAttributeHtml("type", type);
		for (var i in embedOptions) {
			html += ' '+i+'="'+embedOptions[i]+'"';
		}
		html += '></embed>\n';
	} else {
		// ActiveX object tag
		html += '<object classid="clsid:'+(Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].classID)+'"';
		html += getAttributeHtml("id", id) + getAttributeHtml("name", name) + getAttributeHtml("width", width) + getAttributeHtml("height", height) + getAttributeHtml("codebase", (Plugin.PLUGINS[plugin] && Plugin.PLUGINS[plugin].codeBase)) + getAttributeHtml("type", activeXType) + '>\n';
		html += (src) ? '  <param name="src" value="'+src+'">\n' : '';
		for (var i in embedOptions) {
			html += '  <param name="'+i+'" value="'+embedOptions[i]+'" />';
		}
		html += '</object>\n';
	}

	if (target) {
		if (typeof target == "string") target = document.getElementById(target);
			target.innerHTML = html;
		} else {
			document.write(html);
		}
	}

	var getAttributeHtml = function(name, value) {
		return (value) ? (" " + name + "=\"" + value + "\"") : "";
	}

  // Info about known plugins
  this.PLUGINS = {
    "Acrobat": {
      description: "Adobe Acrobat Plugin",
      progID: ["PDF.PdfCtrl.7", "PDF.PdfCtrl.6", "PDF.PdfCtrl.5", "PDF.PdfCtrl.4", "PDF.PdfCtrl.3", "AcroPDF.PDF.1"],
      classID: "CA8A9780-280D-11CF-A24D-444553540000",
      pluginsPage: "http://www.adobe.com/products/acrobat/readstep2.html",
      acceptedMimeTypes: [
        { type: "application/pdf", suffixes: "pdf" },
        { type: "application/vnd.fdf", suffixes: "fdf" },
        { type: "application/vnd.adobe.xfdf", suffixes: "xfdf" },
        { type: "application/vnd.adobe.xdp+xml", suffixes: "xdp" },
        { type: "application/vnd.adobe.xfd+xml", suffixes: "xfd" }
      ]
    },
    "QuickTime": {
      description: "QuickTime Plug-in",
      progID: ["QuickTimeCheckObject.QuickTimeCheck.1", "QuickTime.QuickTime"],
      classID: "02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",
      pluginsPage: "http://www.apple.com/quicktime/download/",
      codeBase: "http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0",
      mimeType: "video/quicktime",
      standardEmbedAttributes: {
        autoplay: "false"
      },
      // embedInfo: http://www.apple.com/quicktime/tutorials/embed.html 
      //            http://developer.apple.com/quicktime/compatibility.html
      getActiveXVersionInfo: function() { 
        var progID = getProgIdForActiveXObject(Plugin.PLUGINS["QuickTime"].progID); 
        var obj = new ActiveXObject(progID);
        var version = (obj && obj.QuickTimeVersion) ? obj.QuickTimeVersion.toString(16) : "";
        return version.substring(0,1) + '.' + version.substring(1,2) + '.' + version.substring(2,3);
      },
      acceptedMimeTypes: [
        { type: "image/tiff", suffixes: "tif,tiff" },
        { type: "image/x-tiff", suffixes: "tif,tiff" },
        { type: "video/x-m4v", suffixes: "m4v" },
        { type: "image/x-macpaint", suffixes: "pntg,pnt,mac" },
        { type: "image/pict", suffixes: "pict,pic,pct" },
        { type: "image/x-pict", suffixes: "pict,pic,pct" },
        { type: "image/x-quicktime", suffixes: "qtif,qti" },
        { type: "image/x-sgi", suffixes: "sgi,rgb" },
        { type: "image/x-targa", suffixes: "targa,tga" },
        { type: "audio/3gpp", suffixes: "3gp,3gpp" },
        { type: "video/3gpp2", suffixes: "3g2,3gp2" },
        { type: "audio/3gpp2", suffixes: "3g2,3gp2" },
        { type: "video/sd-video", suffixes: "sdv" },
        { type: "application/x-mpeg", suffixes: "amc" },
        { type: "video/mp4", suffixes: "mp4" },
        { type: "audio/mp4", suffixes: "mp4" },
        { type: "audio/x-m4a", suffixes: "m4a" },
        { type: "audio/x-m4p", suffixes: "m4p" },
        { type: "audio/x-m4b", suffixes: "m4b" },
        { type: "video/mpeg", suffixes: "mpeg,mpg,m1s,m1v,m1a,m75,m15,mp2,mpm,mpv,mpa" },
        { type: "audio/mpeg", suffixes: "mpeg,mpg,m1s,m1a,mp2,mpm,mpa,m2a" },
        { type: "audio/x-mpeg", suffixes: "mpeg,mpg,m1s,m1a,mp2,mpm,mpa,m2a" },
        { type: "video/3gpp", suffixes: "3gp,3gpp" },
        { type: "audio/x-gsm", suffixes: "gsm" },
        { type: "audio/AMR", suffixes: "AMR" },
        { type: "audio/aac", suffixes: "aac,adts" },
        { type: "audio/x-aac", suffixes: "aac,adts" },
        { type: "audio/x-caf", suffixes: "caf" },
        { type: "video/x-mpeg", suffixes: "mpeg,mpg,m1s,m1v,m1a,m75,m15,mp2,mpm,mpv,mpa" },
        { type: "audio/aiff", suffixes: "aiff,aif,aifc,cdda" },
        { type: "audio/x-aiff", suffixes: "aiff,aif,aifc,cdda" },
        { type: "audio/basic", suffixes: "au,snd,ulw" },
        { type: "audio/mid", suffixes: "mid,midi,smf,kar" },
        { type: "audio/x-midi", suffixes: "mid,midi,smf,kar" },
        { type: "audio/midi", suffixes: "mid,midi,smf,kar" },
        { type: "audio/vnd.qcelp", suffixes: "qcp" },
        { type: "application/sdp", suffixes: "sdp" },
        { type: "application/x-sdp", suffixes: "sdp" },
        { type: "application/x-rtsp", suffixes: "rtsp,rts" },
        { type: "video/quicktime", suffixes: "mov,qt,mqv" },
        { type: "video/flc", suffixes: "flc,fli,cel" },
        { type: "audio/x-wav", suffixes: "wav,bwf" },
        { type: "audio/wav", suffixes: "wav,bwf" }
      ]
    },
    "Director": {
      description: "Macromedia Director",
      progID: ["SWCtl.SWCtl.11","SWCtl.SWCtl.10","SWCtl.SWCtl.9","SWCtl.SWCtl.8","SWCtl.SWCtl.7","SWCtl.SWCtl.6","SWCtl.SWCtl.5","SWCtl.SWCtl.4"],
      classID: "166B1BCA-3F9C-11CF-8075-444553540000",
      pluginsPage: "http://www.macromedia.com/shockwave/download/",
      codeBase: "http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0",
      mimeType: "application/x-director"
    },         
    "Flash": {
      description: "Macromedia Shockwave Flash",
      progID: ["ShockwaveFlash.ShockwaveFlash.9", "ShockwaveFlash.ShockwaveFlash.8.5", "ShockwaveFlash.ShockwaveFlash.8", "ShockwaveFlash.ShockwaveFlash.7", "ShockwaveFlash.ShockwaveFlash.6", "ShockwaveFlash.ShockwaveFlash.5", "ShockwaveFlash.ShockwaveFlash.4"],
      classID: "D27CDB6E-AE6D-11CF-96B8-444553540000",
      pluginsPage: "http://www.macromedia.com/go/getflashplayer",
      codeBase: "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0",
      mimeType: "application/x-shockwave-flash",
      standardEmbedAttributes: {
        quality: "high"
      },
      // embedInfo: http://www.macromedia.com/cfusion/knowledgebase/index.cfm?id=tn_4150
      //            http://www.macromedia.com/cfusion/knowledgebase/index.cfm?id=tn_12701
      acceptedMimeTypes: [
        { type: "application/x-shockwave-flash", suffixes: "swf" },
        { type: "application/futuresplash", suffixes: "spl" }
      ]
    }
  }

	var supportsNavigatorPlugins = function() {
		return (navigator.plugins && (navigator.plugins.length > 0));
	}

	var supportsActiveX = function() {
		return ((typeof 'ActiveXObject' != 'undefined') && (navigator.userAgent.indexOf('Win') != -1));
	}

	var findNavigatorPluginByName = function(name) {
		if (supportsNavigatorPlugins()) {
			for(var i=0;i<navigator.plugins.length;++i) {
				var plugin = navigator.plugins[i];
				if (plugin.name.indexOf(name) != -1) {
					return plugin;
				}
			}
		}
		return null;
	}

	var findPluginName = function(str) {
		for (var pluginName in Plugin.PLUGINS) {
			if (str.indexOf(pluginName) != -1) {
				return pluginName;
			}
		}
		return null;
	}

	var getIEClientCaps = function() {
		var clientcaps = document.getElementById("__Plugin_ClientCaps");
		if (!clientcaps) {
			var clientcaps = document.createElement("DIV");
			clientcaps.id = "__Plugin_ClientCaps";
			if (clientcaps.addBehavior) {
				clientcaps.addBehavior("#default#clientCaps");
				document.body.appendChild(clientcaps);
			}
			clientcaps = document.getElementById("__Plugin_ClientCaps");
		}
		return clientcaps;    
	}

	var getActiveXPluginByClassId = function(classID) {
		if (!classID) return null;
		if (!classID.match(/{[^}]+}/)) classID = "{" + classID + "}";
		var clientcaps = getIEClientCaps();
		try {
			var result = clientcaps.getComponentVersion(classID, "ComponentID")
			return result || null;
		} catch (err) { }
		return null;
	}

	var hasActiveXObject = function(progID) {
		progID = getProgIdForActiveXObject(progID);
		return (progID != null);
	}

	var getProgIdForActiveXObject = function(progID) {
		if (!progID) return null;
		for (var i=0; i<progID.length; i++) {
			try {
				var obj = new ActiveXObject(progID[i]);
				return progID[i] || null;
			}
			catch(e) { }
		}
		return null;
	}

	// accepts plugin or string
	var getVersionFromPlugin = function(plugin) {
		if (!plugin.name) plugin = { name: plugin, description: name };
		var matches = /[\d][\d\.]*/.exec(plugin.name);
		if (matches && plugin.name.indexOf("Java") == -1) return matches[0];
		matches = /[\d\.]+/.exec(plugin.description);
		return matches ? matches[0] : "";
	}
  
};

// helper functions
// for usage without prototype.js
if (!Object.extend) {
	Object.extend = function(destination, source) {
		for (property in source) {
			destination[property] = source[property];
		}
		return destination;
	}
}

// Array functions
Array.contains = function(arr, el) {
	return Array.indexOf(arr, el) != -1;
}

Array.indexOf = function(arr, el) {
	for (var i=0; i<arr.length; i++) {
		if (arr[i] == el) return i;
	}
	return -1;
}

String.encode =
String.prototype.encode = function() {
	var str = this;
	str = str.replace("&", "&amp;");
	str = str.replace("<", "&lt;");
	str = str.replace(">", "&gt;");
	str = str.replace("\"", "&quot;");
	str = str.replace("\n", "");
	return str;
}

if (!window.Plugin) {
	var Plugin = new Object();
}
Object.extend(Plugin, (new PluginFactory()));
