/**
 * Flash (http://jquery.lukelutman.com/plugins/flash)
 * A jQuery plugin for embedding Flash movies.
 * 
 * Version 1.0
 * November 9th, 2006
 *
 * Copyright (c) 2006 Luke Lutman (http://www.lukelutman.com)
 * Dual licensed under the MIT and GPL licenses.
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.opensource.org/licenses/gpl-license.php
 * 
 * Inspired by:
 * SWFObject (http://blog.deconcept.com/swfobject/)
 * UFO (http://www.bobbyvandersluis.com/ufo/)
 * sIFR (http://www.mikeindustries.com/sifr/)
 * 
 * IMPORTANT: 
 * The packed version of jQuery breaks ActiveX control
 * activation in Internet Explorer. Use JSMin to minifiy
 * jQuery (see: http://jquery.lukelutman.com/plugins/flash#activex).
 *
 **/ 
(function(){

	var $self;
	
	var REPLACE_COUNT = 0;

	/**
	 * 
	 * @desc Replace matching elements with a flash movie.
	 * @author Luke Lutman
	 * @version 1.0.1
	 *
	 * @name flash
	 * @param Hash htmlOptions Options for the embed/object tag.
	 * @param Hash pluginOptions Options for detecting/updating the Flash plugin (optional).
	 * @param Function replace Custom block called for each matched element if flash is installed (optional).
	 * @param Function update Custom block called for each matched if flash isn't installed (optional).
	 * @type jQuery
	 *
	 * @cat plugins/flash
	 * 
	 * @example $('#hello').flash({ src: 'hello.swf' });
	 * @desc Embed a Flash movie.
	 *
	 * @example $('#hello').flash({ src: 'hello.swf' }, { version: 8 });
	 * @desc Embed a Flash 8 movie.
	 *
	 * @example $('#hello').flash({ src: 'hello.swf' }, { expressInstall: true });
	 * @desc Embed a Flash movie using Express Install if flash isn't installed.
	 *
	 * @example $('#hello').flash({ src: 'hello.swf' }, { update: false });
	 * @desc Embed a Flash movie, don't show an update message if Flash isn't installed.
	 *
	**/
	$self = jQuery.fn.flash = function(htmlOptions, pluginOptions, replace, update) {
	
		// Set the default block.
		var block = replace || $self.replace;
	
		// Merge the default and passed plugin options.
		pluginOptions = $self.copy($self.pluginOptions, pluginOptions);
	
		// Detect Flash.
		if (!$self.hasFlash(pluginOptions.version)) {
			// Use Express Install (if specified and Flash plugin 6,0,65 or higher is installed).
			if (pluginOptions.expressInstall && $self.hasFlash(6,0,65)) {
				// Add the necessary flashvars (merged later).
				var expressInstallOptions = {
					flashvars: {  	
						MMredirectURL: location,
						MMplayerType: 'PlugIn',
						MMdoctitle: jQuery('title').text() 
					}					
				};
			// Ask the user to update (if specified).
			} else if (pluginOptions.update) {
				// Change the block to insert the update message instead of the flash movie.
				block = update || $self.update;
			// Fail
			} else {
				// The required version of flash isn't installed.
				// Express Install is turned off, or flash 6,0,65 isn't installed.
				// Update is turned off.
				// Return without doing anything.
				return this;
			}
		}
	
		// Merge the default, express install and passed html options.
		htmlOptions = $self.copy($self.htmlOptions, expressInstallOptions, htmlOptions);
	
		// Invoke $block (with a copy of the merged html options) for each element.
		return this.each(function(){
			block.call(this, $self.copy(htmlOptions));
		});
	
	};

	/**
	 *
	 * @name flash.copy
	 * @desc Copy an arbitrary number of objects into a new object.
	 * @type Object
	 * 
	 * @example $self.copy({ foo: 1 }, { bar: 2 });
	 * @result { foo: 1, bar: 2 };
	 *
	**/
	$self.copy = function() {
		var options = {}, flashvars = {};
		for(var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
		
			if (arg == undefined) {
				continue;
			}
		
			jQuery.extend(options, arg);
			// don't clobber one flash vars object with another
			// merge them instead
			if (arg.flashvars == undefined) {
				continue;
			}
		
			jQuery.extend(flashvars, arg.flashvars);
		}
	
		options.flashvars = flashvars;
		return options;
	};

/*
	 * @name flash.hasFlash
	 * @desc Check if a specific version of the Flash plugin is installed
	 * @type Boolean
	 *
	**/
	$self.hasFlash = function() {
		// look for a flag in the query string to bypass flash detection
		if ((/hasFlash\=true/).test(location)) {
			return true;
		}
	
		if ((/hasFlash\=false/).test(location)) {
			return false;
		}
	
		var pv = $self.hasFlash.playerVersion().match(/\d+/g);
		var rv = String([arguments[0], arguments[1], arguments[2]]).match(/\d+/g) || String($self.pluginOptions.version).match(/\d+/g);
	
		for (var i = 0; i < 3; i++) {
			pv[i] = parseInt(pv[i] || 0);
			rv[i] = parseInt(rv[i] || 0);
			// player is less than required
			if (pv[i] < rv[i]) {
				return false;
			}
		
			// player is greater than required
			if (pv[i] > rv[i]) {
				return true;
			}
		}
	
		// major version, minor version and revision match exactly
		return true;
	};

	/**
	 *
	 * @name flash.hasFlash.playerVersion
	 * @desc Get the version of the installed Flash plugin.
	 * @type String
	 *
	**/
	$self.hasFlash.playerVersion = function() {
	
		if (window.ActiveXObject) {
			try {
				// avoid fp6 minor version lookup issues
				// see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
				var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
				try {
					axo.AllowScriptAccess = 'always';
				} catch(e) { 
					return '6,0,0';
				}
				
				return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
			} catch(e) {}
		
		} else if (navigator.mimeTypes) {
			try {
				if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin) {
					return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
				}
			} catch(e) {}
		
		}
	
		return '0,0,0';
	};

	/**
	 *
	 * @name flash.htmlOptions
	 * @desc The default set of options for the object or embed tag.
	 *
	**/
	$self.htmlOptions = {
		height : 240,
		flashvars : {},
		pluginspage : 'http://www.adobe.com/go/getflashplayer',
		src : '#',
		type : 'application/x-shockwave-flash',
		width : 320		
	};

	/**
	 *
	 * @name flash.pluginOptions
	 * @desc The default set of options for checking/updating the flash Plugin.
	 *
	**/
	$self.pluginOptions = {
		expressInstall : false,
		update : true,
		version : '6.0.65'
	};

	/**
	 *
	 * @name flash.replace
	 * @desc The default method for replacing an element with a Flash movie.
	 *
	**/
	$self.replace = function(htmlOptions) {
		var replaced = '<div class="alt">' + this.innerHTML + '</div>';
		if ($.browser.msie) {
			var spanToReplace = jQuery(this).addClass('flash-replaced').html('<span class="replace-me"></span>' + replaced).find(".replace-me")[0];
			if (spanToReplace) {
				spanToReplace.outerHTML = $self.transform(htmlOptions);
			}
		} else {
			this.innerHTML = replaced;
			jQuery(this).addClass('flash-replaced').prepend($self.transform(htmlOptions));
		}
	};

	/**
	 *
	 * @name flash.update
	 * @desc The default method for replacing an element with an update message.
	 *
	**/
	$self.update = function(htmlOptions) {
		return;
		var url = String(location).split('?');
		url.splice(1, 0, '?hasFlash=true&');
		url = url.join('');
		var msg = '<p>This content requires the Flash Player. <a href="http://www.adobe.com/go/getflashplayer">Download Flash Player</a>. Already have Flash Player? <a href="' + url + '">Click here.</a></p>';
		this.innerHTML = '<span class="alt">' + this.innerHTML + '</span>';
		jQuery(this).addClass('flash-update').prepend(msg);
	};

	/**
	 *
	 * @desc Convert a hash of html options to a string of attributes, using Function.apply(). 
	 * @example toAttributeString.apply(htmlOptions)
	 * @result foo="bar" foo="bar"
	 *
	**/
	function toAttributeString() {
		var s = '';
		for (var key in this) {
			if (typeof this[key] != 'function') {
				s += key + '="' + this[key] + '" ';
			}
		}
		
		return s;		
	}

	/**
	 *
	 * @desc Convert a hash of flashvars to a url-encoded string, using Function.apply(). 
	 * @example toFlashvarsString.apply(flashvarsObject)
	 * @result foo=bar&foo=bar
	 *
	**/
	function toFlashvarsString() {
		var s = '';
		for (var key in this) {
			if (typeof this[key] != 'function') {
				s += key + '=' + encodeURIComponent(this[key]) + '&';
			}
		}
	
		return s.replace(/&$/, '');		
	}

	/**
	 *
	 * @name flash.transform
	 * @desc Transform a set of html options into an embed tag.
	 * @type String 
	 *
	 * @example $self.transform(htmlOptions)
	 * @result <embed src="foo.swf" ... />
	 *
	 * Note: The embed tag is NOT standards-compliant, but it 
	 * works in all current browsers. flash.transform can be
	 * overwritten with a custom function to generate more 
	 * standards-compliant markup.
	 *
	**/
	$self.transform = function(htmlOptions) {
		htmlOptions.toString = toAttributeString;
		if (htmlOptions.flashvars) {
			htmlOptions.flashvars.toString = toFlashvarsString;
		}
		
		if ($.browser.msie) {
			var params = ieOptions(htmlOptions);
			var attrs = params.shift();
			var params = params[0];
			
			var attrStrings = [];
			for (var key in attrs) {
				attrStrings.push(key + '="' + attrs[key] + '"');
			}
			
			var obj = '<object ' + attrStrings.join(" ") + '>';
		
			var paramsStrings = [];
			for (var key in params) {
				paramsStrings.push('<param name="' + key + '" value="' + params[key] + '" />');
			}
			
			obj += paramsStrings.join(" ") + '</object>';
			return obj;
		} else {
			return '<embed ' + String(htmlOptions) + '/>';	
		}
	};
	
	function ieOptions(options) {
		$.extend(options, {
			classid: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
		});
		
		var attrNames = ["width", "height", "id", "name", "class", "classid"];
		var ignore = ["pluginspage", "type", "toString"];
		var attrs = {};
		var params = {};
		
		for (var key in options) {
			if ($.inArray(key, ignore) == -1) {
				if ($.inArray(key, attrNames) > -1) {
					attrs[key] = options[key];
				} else {
					var realkey = key === "src" ? "movie" : key;
					params[realkey] = options[key];
				}
			}
		}
		
		if (! attrs.id) {
			attrs.id = "jquery-flash-" + REPLACE_COUNT++;
		}
		
		return [attrs, params];
	};

	/**
	 *
	 * Flash Player 9 Fix (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
	 *
	**/
	if (window.attachEvent) {
		window.attachEvent("onbeforeunload", function(){
			__flash__removeCallback = function(instance, name) {
				if (instance && name) {
					instance[name] = null;
				}
			};
			
			__flash_unloadHandler = function() {};
			__flash_savedUnloadHandler = function() {};
		});
	}
	
})();
