Flash = Class.create();


/**
 * a list of all settings that are not coppied directly from the 
 * settings object to the rendered output, 
 * either because they are syntax related, or because they are for 
 * internal use
 */
Flash.nonAttributeSettings = ['targetElement', 'log', 'id'
							 ,'classid','codebase','type','pluginspage', 'src'
							 ];
/**
 * a list of all settings that must be presented as <param> in <object> syntax
 * @type Array
 */
Flash.objectParams = ['bgcolor','quality','wmode','base','menu'
                     ,'scale','swremote','loop','salign'
                     ,'devicefont','embedmovie','seemlesstabbing'
                     ,'allowFullScreen'
					 ,'flashvars','allowscriptaccess'];

/**
 * default settings 
 * @type Object
 */
Flash.defaultSettings = 
	{	classid		: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
	,	codebase	: "https://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0"
	,	type		: "application/x-shockwave-flash"
	,	pluginspage	: "https://www.macromedia.com/go/getflashplayer"
	}

/**
 * @param {String} sSwfUrl 
 *  The Url to the presented swf file
 * @param {Object(optional)} oSettings 
 *	Supports the folowing settings<ol>
 *  <li>height
 *  <li>width
 *  <li>menu
 *  <li>wmode
 *  <li>quality
 *  <li>allowScriptAccess
 *  <li>wmode
 *  <li>base 
 *  </ol>
 * @param {String(optional)} sNoSettingsAttributes
 * This optional parameter allows the oSettings parameter to be provided 
 * with properties that should not be copied as flash settings. <br>
 * The usage is when the passed object is used as a setting object for other uses,
 * and contain attributes that must not be coppied to the flash object
 */
Flash.prototype.initialize = function(sSwfUrl, oSettings, sNoSettingsAttributes)
{
	/**
	 * The displayed movie
	 * @type String
	 */
	this.swfUrl = sSwfUrl;
	/**
	 * the settings object
	 * @type Object
	 */
	this.settings = Object.extend({}, this.constructor.defaultSettings);
	sNoSettingsAttributes = "," + sNoSettingsAttributes + ",";
	var each;
	for(each in oSettings)
		if (sNoSettingsAttributes.indexOf("," + each + ",") == -1)
			this.settings[each] = oSettings[each];

	/**
	 * The logger
	 * @type Log4Js.Logger
	 */
	this.log = this.settings.log || new Log4Js.Logger(this.settings.id || this.settings.targetElement &&  this.settings.targetElement != document || this.swfUrl || "Blank Flash instance")
	delete this.settings.log;

	/**
	 * the target DOM Container (or its ID as a string)
	 * @type DOMContainer|String
	 */
	this.targetElement = this.settings.targetElement;
	delete this.settings.targetElement;

	/**
	 * Holds the syntax to use ( object | embed ), based on the detected browser type
	 * <code>object</code> syntax is used on IE based browsers, on Windows only, except Opera.<br>
	 * The var is initiated in {@link Flash#render} method.
	 * @type String
	 */
	this.useSyntax = null;

	/**
	 * The HTML prepared and returned by {@link Flash#render}
	 * @type String
	 */
	 this.HTML = null;

	/**
	 * Holds all the key-values of all attributes for the tag.
	 * key-value pairs are collected by the used syntax (object or embed)
	 * @type prototype:Hash
	 */
	this.attributes = $H();

	/**
	 * Holds all the key-values of all params for the tag
	 * key-value pairs are collected by the used syntax
	 * @type prototype:Hash
	 */
	this.params = $H();

	//if the targetElement and the swf are provided - perform the render 
	if (this.targetElement && this.swfUrl) 
	{
		this.render(this.targetElement);
	}

}
/**
 * returns the syntax for the run-time environment.
 * <code>object</code> syntax is used on IE based browsers, on Windows only, except Opera.<br>
 * @type String
 * @returns 'object' or 'embed', depening on the run-time environment
 * @overridable
 */
Flash.prototype.getTagSyntax = function()
{
	var isIE  = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
	var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
	var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;

	return (isIE && isWin && !isOpera)? 'object': 'embed';
}

/**
 * copy all supported attributes that are populated in settings to attributes
 * @private
 */
Flash.prototype.prv_prepareAttributes = function()
{
	var attribute; 
	for(attribute in this.settings)
	{
		if(Flash.nonAttributeSettings.indexOf(attribute) != -1) continue;
		this.attributes[attribute] = this.settings[attribute];
	}
}

/**
 * move attributes to param for '<object>' syntax
 * @private
 */
Flash.prototype.prv_moveAttributesToParams = function()
{

	var attribute;
	for (attribute in this.attributes)
	{
		if (typeof(this.attributes[attribute]) == 'function' ) continue;
		if( Flash.objectParams.indexOf( attribute.toLowerCase() ) != -1)
		{
			this.params[attribute] = this.attributes[attribute];
			delete this.attributes[attribute];
		}
	}
}

/**
 * emmits into the provided <codE>out</code> array the output of the 
 * attributes prepared at this.attributes according the prepared syntax
 * @private
 */
Flash.prototype.prv_renderAttributes = function(out)
{
	this.attributes.each(
		function(kv,i)
		{
			out[out.length] = ' ';
			out[out.length] = kv[0];
			out[out.length] = '="';
			out[out.length] = kv[1];
//			out[out.length] = escape(kv[1]);
			out[out.length] = '"';
		}
	);
	out[out.length] = ">\n";
}
/**
 *
 */
Flash.prototype.prv_renderParams = function(out)
{
	if(this.useSyntax != 'object') return;
	this.params.movie = this.swfUrl;
	this.params.each(
		function(kv,i)
		{
			out[out.length] = '<param name="';
			out[out.length] = kv[0];
			out[out.length] = '" value="';
			out[out.length] = kv[1];
			//out[out.length] = escape(kv[1]);
			out[out.length] = '"/>\n';
		}
	);
}
/**
 * bag of method references used to open the tag, according to the required syntax
 * on the constructor, the bag is overriden by the reference of the method that implements the required syntax.
 * @private
 */
Flash.prototype.prv_renderOpenTag = 
{	object: function(out)
			{
				out[out.length] = '<object classid="';
				out[out.length] = this.settings.classid;
				out[out.length] = '"\n\t codebase="'; 
				out[out.length] = this.settings.codebase;
				out[out.length] = '"\n\t id="'; 
				out[out.length] = this.objectID;
				out[out.length] = '"\n\t';
			}
,	embed : function(out)
			{
				out[0] = out[1] = out[2] = "";
				out.push('<embed type="');
				out.push(this.settings.type);
				out.push('" pluginspage="');
				out.push(this.settings.pluginspage);
				out.push('" name="');
				out.push(this.objectID);
 				out.push('" src="');
				out.push(this.swfUrl);
				out.push('"');
			}
}

/**
 * bag of method references used to close the tag, according to the required syntax
 * on the constructor, the bag is overriden by the reference of the method that implements the required syntax.
 * @private
 */
Flash.prototype.prv_renderCloseTag = 
{	object: function(out)
			{
				out[out.length] = "</object>";
			}

,	embed : function(out)
			{
				out.push("</embed>");
			}
}


/**
 * returns the HTML for the flash-tag in the syntax relevant for the run-time environment 
 * if target element is provided - renders the HTML into it.<br>
 * <code>targetElement</codE> can be provided as an argument, or as entry on <code>settings</code> arguments to the constructor.
 * The returned HTML is also kept on {@link Flash#HTML}.
 *
 * @param {String} targetElement
 * the ID of the DOM Container to render the Flash tag into.<br>
 * When not provided - <code>this.settings.targetElement</code> is used.
 *
 * @type String
 * @returns the prepared HTML for the flash tag
 */
Flash.prototype.render = function(targetElement)
{
	//detect the right syntax
	this.useSyntax = this.getTagSyntax();

	//get browser-dependent open and close tag method-references
	if(typeof(this.prv_renderOpenTag  ) != 'function') this.prv_renderOpenTag = this.prv_renderOpenTag[this.useSyntax];
	if(typeof(this.prv_renderCloseTag ) != 'function') this.prv_renderCloseTag = this.prv_renderCloseTag[this.useSyntax];

	//copy all supported attributes that are populated in settings to attributes
	this.prv_prepareAttributes();

	//move attributes to param for '<object>' syntax
	if (this.useSyntax == 'object')	this.prv_moveAttributesToParams();

	//find the target element;	
	if (!targetElement) 
		targetElement = 
			this.targetElement = 
				this.settings.targetElement || this.targetElement;

	//if the object is not in the DOM yet 
	if( targetElement && !$(targetElement) )
	{
		//if the name exists in postLoadRender collection - we're on the onLoad already
		if(!Flash.postLoadRender[targetElement])
		{
			Flash.postLoadRender[targetElement] =
				Flash.postLoadRender[Flash.postLoadRender.length] =
					this;
			return;
		}
	}

	//get the object
	targetElement = $(targetElement);

	//get or create objectID
	this.objectID = this.settings.id || ((targetElement)? targetElement.id + "_Flash" : "avatar_Flash");

	var out = [];
	this.prv_renderOpenTag(out);
	this.prv_renderAttributes(out);
	this.prv_renderParams(out);
	this.prv_renderCloseTag(out);

	out = out.join("");

	var o = $(this.objectID);
	if (o)
	{
		o.parentNode.removeChild(o);
	}

	if( targetElement === document)
		document.write(out);
	else if( $(targetElement) )
		targetElement.innerHTML = out;

	this.HTML = out;

	return out;
}
/**
 * a collection of all Flash instances that were provided 
 * a target-element and a swf name, but thier target was not 
 * found in the dom by the time of the eval of this file.
 * @type Array
 * @private
 */
Flash.postLoadRender = [];

/**
 * fires on load of the window. 
 * attached by Event.observe with the eval of this file
 */
Flash.renderPostLoad = function()
{
	var arr = Flash.postLoadRender;
	for(var i = 0 ; i < arr.length; i++) arr[i].render();
}
Event.observe(window, "load", Flash.renderPostLoad);


/**
 * gets a reference to the node in the dom of the <code>object</code> or <code>embed</code>
 *
 * @param {String(optional)} movieName
 * The ID of the object tag to retrieve. When not provided - assumes the current objectID.
 */
Flash.prototype.getFlashMovieObject = function (movieName)
{
	if( !movieName ) movieName = this.objectID;

	if (window.document[movieName]) 
	{
		return window.document[movieName];
	}
	if (navigator.appName.indexOf("Microsoft Internet")==-1)
	{
		if (document.embeds && document.embeds[movieName])
			return document.embeds[movieName]; 
	}
	else // if (navigator.appName.indexOf("Microsoft Internet")!=-1)
	{
		return document.getElementById(movieName);
	}
}