    // Copyright 2008 www.flexcapacitor.com, www.drumbeatinsight.com 
    // Version 4.0
    
    // global array of html elements
    var fcHTMLControls = new Array();
    var fcEventTimeoutInterval = 200;
    var fcExistingOnBeforeUnload;
    var fcRelativeMovie = false;
	
    // blur xinha editor
    function fcAddBlurHandler() {
		this._doc.xinhaEditor = this;
		Xinha._addEvent(this._doc, "blur", function() {
			var movie = fcGetMovieById(this.xinhaEditor.movieId);
			//movie.xinhaBlur();
			window.focus();
			movie.focus();
		});
    }
    
	// add HTML element to the page
    function fcAddChild(o) {
    	var movie = fcGetMovieById(o.movieId);
    	fcHTMLControls.movieId = o.movieId;
    	
    	if (String(o.chrome)=="true") {
    		fcAddChildPopUp(o);
    	}
    	else if (o.type=="division") {
    		fcAddChildDivision(o);
    	}
    	else if (o.type=="iframe") {
    		fcAddChildIFrame(o);
    	}
    	else if (o.type=="editor") {
    		fcAddChildEditor(o);
    	}
    }
    
    // add division to the page
    function fcAddChildDivision(o) {
		fcGetIncludes(o.includesBefore);
		var newElement = document.createElement("div");
		newElement.id = o.id;
		newElement.name = o.name;
		newElement.movieId = o.movieId;
		
		newElement.style.position = o.position;
		
		if (String(o.fitToContentHeight)=="true") {
			o.height = "";
		}
		fcSetSize(newElement,o.width,o.height);
		fcMoveElementTo(newElement,o.x,o.y);
		newElement.style.backgroundColor = "#" + o.backgroundColor;
		newElement.style.padding = o.padding;
		newElement.style.margin = "0px";
		// always 0px - do not add a border to the container div tag
		// add a border in mxml or add a child div tag in the htmlText property and add a border to that
		newElement.style.border = o.border;
		newElement.innerHTML = o.htmlText;
		
		document.body.appendChild(newElement);
		
		fcAddToGlobalArray(newElement, o);
		fcSetScrollPolicyById(o.id, o.htmlScrollPolicy);
		
		fcGetIncludes(o.includesAfter);
		
		if (String(o.visible)=="false") {
			fcHide(o.id,true,false);
		}
		
		fcDelayedDispatchEvent("htmlCreationComplete", o.movieId, o.id, fcEventTimeoutInterval);
    }
    
    // add an editor to the page
    function fcAddChildEditor(o) {
    	// Add new editors here
		// There are three places to add new editor support. 
		// here, getHTML, setHTML and if possible, call htmlCreationComplete when the editor has been created
		// see FCKeditor_OnComplete for an example
		
    	// if the editor is generated with the same id, same textarea should be used
		var el = document.getElementById(o.id);
		if (el!=null) return;
		
		// we add this control early on editors 
		fcHTMLControls[o.id] = new Object();
	
		fcGetIncludes(o.includesBefore);
		var editorName = o.name + "Editor";
		var newElement = document.createElement("div");
		newElement.id = o.id;
		newElement.name = o.name;
		newElement.movieId = o.movieId;
		
		newElement.style.position = o.position;
		fcSetSize(newElement,o.width,o.height);
		fcMoveElementTo(newElement,o.x,o.y);
		newElement.style.backgroundColor = "#" + o.backgroundColor;
		newElement.style.padding = o.padding;
		newElement.style.margin = "0px";
		newElement.style.border = o.border;
		
		var textareaElement = document.createElement("textarea");
		textareaElement.id = editorName;
		textareaElement.name = editorName;
		textareaElement.value = o.htmlText;
		fcSetSize(textareaElement,"100%","100%");
		newElement.appendChild(textareaElement);
		
		document.body.appendChild(newElement);
		fcSetScrollPolicyById(o.id, o.htmlScrollPolicy);
		
		// add additional editor support here
		if (o.editorType=="fckeditor") {
			// Create an instance of FCKeditor (using the target textarea as the name).
			var oFCKeditor = new FCKeditor( editorName ) ;
			oFCKeditor.BasePath = o.editorPath;
			if (o.configPath) {
				oFCKeditor.Config["CustomConfigurationsPath"] = o.configPath +"?" + ( new Date() * 1 ) ;
			}
			oFCKeditor.Width = '100%';
			oFCKeditor.Height = '100%';
			oFCKeditor.ReplaceTextarea();
			
			fcHTMLControls[o.id].editor = oFCKeditor;
		}
		else if (o.editorType=="tinymce") {
		    var elm = document.getElementById(o.id);
		    
			if (typeof o.editorOptions == "string") {
				// crashes firefox??? maybe set a timeout?
				//tinyMCE.init({mode:"exact",theme:"simple"});
			}
			else {
				tinyMCE.init(o.editorOptions);
			}
			
			if (tinyMCE.getInstanceById(editorName) == null) {
				// cannot find editor sometimes. causes crash. maybe set a timeout?
				//tinyMCE.execCommand('mceAddControl', false, editorName);
			}
			fcHTMLControls[o.id].editor = elm
			fcHTMLControls[o.id].loaded = true;
			// when editor is ready dispatch creation complete
			//fcDelayedDispatchEvent("htmlCreationComplete", o.movieId, o.id, fcEventTimeoutInterval);
		}
		else if (o.editorType=="xinha") {
			// if script not declared use only textarea
			if (typeof Xinha != "undefined") {
				var c = new Xinha.Config();
				c.showLoading = true;
				var ed = new Xinha(editorName, c);
				fcHTMLControls[o.id].editor = ed
				ed.movieId = o.movieId;
				ed.generate();
				ed._onGenerate = fcAddBlurHandler;
				ed.whenDocReady = function(e) {
					fcHTMLControls[o.id].loaded = true;
					fcDelayedDispatchEvent("htmlCreationComplete", o.movieId, o.id, fcEventTimeoutInterval);
				}
			} else {
				fcHTMLControls[o.id].editor = textareaElement;
			}
		}
		
		fcAddToGlobalArray(newElement, o);
		
		if (String(o.visible)=="false") {
			fcHide(o.id,true,false);
		}
		
    }
    
    // add iframe to the page
    function fcAddChildIFrame(o) {
		var newElement = document.createElement("iframe");
		newElement.id = o.id;
		newElement.name = o.name;
		newElement.movieId = o.movieId;
		newElement.width = o.width;
		newElement.height = o.height;
		newElement.frameBorder = o.frameborder;
		newElement.style.position = o.position;
		fcSetSize(newElement,o.width,o.height);
		fcMoveElementTo(newElement,o.x,o.y);
		//newElement.style.backgroundColor = "transparent";
		// always 0px - do not add a border to the iframe itself
		// add a child div and add a border to that or add border in mxml
		newElement.style.border = o.border;
		newElement.style.padding = o.padding;
		newElement.src = o.source;
		
		//  use innerHTML or DOM element creation methods to put content into body
		document.body.appendChild(newElement);
		
		newElement.onload = new function() {
			// set a flag so the application knows the page is loaded
			fcDelayedDispatchEvent("htmlCreationComplete", o.movieId, o.id, fcEventTimeoutInterval);
		}
		
		newElement.onunload = new function() {
			// set a flag so the application knows the page is unloading
			// can we purge this from memory here?
			fcDispatchEvent("htmlUnload", o.movieId, o.id);
		}
		
		fcAddToGlobalArray(newElement, o);
		
		if (String(o.visible)=="false") {
			fcHide(o.id,true,false);
		}
		
    }
    
    // add mocha ui pop up to the page
    function fcAddChildPopUp(o) {
		fcGetIncludes(o.includesBefore);
		var p = new Object();
		p.id = o.id;
		p.content = o.htmlText;
		p.x = o.x;
		p.y = o.y;
		if (String(o.centerPopUp)!="true") {
			p.x = o.x;
			p.y = o.y;
		}
		p.contentURL = String(o.source);
		p.bodyBgColor = "" + o.backgroundColor;
		p.title = o.title;
		p.modal = o.modal;
		p.width = o.width;
		p.height = o.height;
		p.minimizable = o.minimizable;
		p.maximizable = o.maximizable;
		//p.paddingVertical = parseInt(o.padding); 10
		//p.paddingHorizontal = parseInt(o.padding); 12
		
		p.onContentLoaded = function (e) {
			fcDelayedDispatchEvent("htmlPopUpContentLoaded", o.movieId, o.id, fcEventTimeoutInterval)
		}
		p.onFocus = function (e) {
			fcDispatchEvent("htmlPopUpFocus", o.movieId, o.id)
		}
		p.onResize = function (e) {
			fcDispatchEvent("htmlPopUpResize", o.movieId, o.id)
		}
		p.onMinimize = function (e) {
			fcDispatchEvent("htmlPopUpMinimize", o.movieId, o.id)
		}
		p.onMaximize = function (e) {
			fcDispatchEvent("htmlPopUpMaximize", o.movieId, o.id)
		}
		p.onClose = function (e) {
			// we need to listen to this event and handle it in flex html manager class for alerts
			fcDispatchEvent("htmlPopUpClose", o.movieId, o.id)
		}
		p.onCloseComplete = function (e) {
			fcDispatchEvent("htmlPopUpCloseComplete", o.movieId, o.id)
		}
		
		// html, xhr, or iframe
		if (o.type == "division") {
			p.loadMethod = "html";
		}
		else if (o.type == "iframe") {
			p.loadMethod = "iframe";
		}
		else if (o.type == "xhr") {
			p.loadMethod = "xhr";
		}
		
		if (String(o.chrome)=="true" && document.mochaUI) {
			document.mochaUI.newWindow(p, false);
		}
		
		fcAddToGlobalArray(fcGetElement(o.id), o);
		
		fcGetIncludes(o.includesAfter);
		
		if (String(o.visible)=="false") {
			//fcHide(o.id,true,false);
		}
		
		// dispatch when window is created
		fcDelayedDispatchEvent("htmlCreationComplete", o.movieId, o.id, fcEventTimeoutInterval);
		if (String(o.alert)=="true") {
			var newElement = document.createElement("div");
			newElement.id = o.id + "_alert_buttons";
			newElement.movieId = o.movieId;
			//newElement.style.position = "absolute";
			//newElement.style.bottom = "6px";
			//newElement.style.right = "6px";
			newElement.className = "mochaAlertButtons"; // set in css/mocha.css
			
			for (var i=0;i<o.alertButtons.length;i++) {
				var button = document.createElement("input");
				var value = o.alertButtons[i];
				button.type = "button";
				button.value = value;
				button.className = "mochaAlertButton"; // set in css/mocha.css
				
				button.onclick = function(e) {
					fcDispatchEvent2("htmlAlertHandler", o.movieId, o.id, this.value);
					var el = fcGetElement(o.id);
					if(document.mochaUI) {
						document.mochaUI.closeWindow(el);
					}
				}
				newElement.appendChild(button);
			}
			
			var el = fcGetElement(o.id);
			el.appendChild(newElement);
		}
    }
    
	//usage: var currentTarget = fcEventTrigger (e);
    function fcEventTrigger (e) {
    	if (!e) { e = event; }
    	return e.target || e.srcElement;
	}
    
	// add to associative array - do we need to make any changes for garbage collecting?
	function fcAddToGlobalArray(el, o) {
		var newElement = new Object();
		newElement.element = el; // could cause memory leak do we need it?
		newElement.id = o.id;
		newElement.loaded = false;
		newElement.type = o.type;
		newElement.popUp = o.chrome;
		newElement.alert = o.alert;
		newElement.movieId = o.movieId;
		if (o.type == "editor") {
			// set first in addChildEditor
			newElement.editor = fcHTMLControls[o.id].editor;
		}
		else {
			newElement.editor = "";
		}
		
		fcHTMLControls[o.id] = newElement;
	}
	
	// evaluates a script and returns the value - note use ExternalInterface to call functions
	function fcCallScript(str) {
		try {
			var something = eval(str);
			return something;
		}
		catch(e) {
			return false;
		}
	}
	
    // dispatch creation complete event 
	function fcDispatchEvent(eventName, movieId, id) {
		var movie = fcGetMovieById(movieId);
		movie[eventName](id);
	}
	
	// dispatch creation complete event 
	function fcDispatchEvent2(eventName, movieId, id, value) {
		var movie = fcGetMovieById(movieId);
		movie[eventName](id, value);
	}

	// call actionscript function after specified delay
    // we do this to give the browser and flash time to sync
	function fcDelayedDispatchEvent(eventName,movieId,id,delay) {
		setTimeout("fcDispatchEvent('"+eventName+"','"+movieId+"','"+id+"')",delay);
	}

	// handle when flash text field gets focus, text field in iframe is greedy
	function fcDefocus(movieId) {
		var movie = fcGetMovieById(movieId);
		window.focus();
		movie.focus();
	}
	
	// finds the absolute position of the swf movie
	function fcFindPosition(obj) {
		var left = obj.offsetLeft;
		var top = obj.offsetTop;
		if (obj.offsetParent) {
			var parentPos = this.fcFindPosition(obj.offsetParent);
			if (parentPos[0]) left += parentPos[0];
  			if (parentPos[1]) top += parentPos[1];
 		}
		return [left,top];
 	}
    
	// The FCKeditor_OnComplete function is a special function called everytime an
	// editor instance is completely loaded and available for API interactions.
	function FCKeditor_OnComplete(editorInstance) {
		var editor = fcGetElement(editorInstance.Name);
		if (editor) {
			fcHTMLControls[editor.parentNode.id].loaded = true;			
			fcDelayedDispatchEvent("htmlCreationComplete", editor.parentNode.movieId, editor.parentNode.id, fcEventTimeoutInterval);
		}
	}
	
	// gets the element by name
	function fcGetElement(id) {
		return document.getElementById(id);
	}
	
	// cannot get height of pages loaded from different domains
	// ie, page is hosted at www.yoursite.com and you load www.google.com will fail with return value of -1
	// works in ff and ie. not tested in mac browsers - 
	function fcGetElementHeight(id){
		var el = fcGetElement(id);
		moz = (document.getElementById && !document.all);
		
		if (el) {
		
			// check the height value
			try {
			
				/*** return div height ***/
				if (el.nodeName.toLowerCase()=="div") {
					var scrollHeight = el.scrollHeight;
					var divHeight = el.style.height;
					divHeight = (scrollHeight > parseInt(divHeight)) ? scrollHeight : divHeight;
					return divHeight;
				}
				
				/*** return iframe height ***/
				//moz
				if (moz) {
					return el.contentDocument.body.scrollHeight;
				}
				else if (el.Document) {
					return el.Document.body.scrollHeight;
				}
			}
			catch(e) {
				//An error is raised if the content in the iframe is not from the same domain as the swf
				//alert('Error: ' + e.number + '; ' + e.description+'\nPossibly - Cannot access height of iframe because the content is from another dudes domain');
				return -1;
			}
		}
	}

	// get property value
	function fcGetElementValue(id, elProperty){
		
		// if periods are in the name assume absolute path 
		// otherwise assume element id
		if (id.indexOf('.')!=-1) {
			var newArr = id.split('.');
			var elValue = "";
			
			try {
				el = window;
				for (var i=0;i < newArr.length;i++) {
					el = el[newArr[i]];
				}
				return el;
			}
			catch (e) {
				//alert("Whoooops!! Cant find " + id);
				// should return null or undefined here
				return -1;
			}
		}
		else {
			// try and get property value
			try {
				var el = fcGetElement(id);
				var elValue = el[elProperty];
				return elValue;
			}
			catch(e) {
				//alert("Error: Can't find " + id + "." + elProperty);
				// should return null or undefined here
				return -1;
			}
		}
	}
	
	// get HTML content
	function fcGetHTML(id, elementType, editorType, chrome) {
		var el = fcGetElement(id);
		if (el!=null) {
			
			if (elementType =="division" && String(chrome)=="true") {
				var elContent = fcGetElement(id + "_content");
				if (elContent != null) {
					return String(elContent.innerHTML);
				}
			}
			else if (elementType=="division") {
				return el.innerHTML;
			}
			else if (elementType == "editor") {
				var oEditor;
				
				// add additional editor support here
				if (editorType=="fckeditor") {
					if ( typeof( FCKeditorAPI ) != 'undefined' ) {
						oEditor = FCKeditorAPI.GetInstance( id + "Editor" );
						if ( oEditor )	{
							// Get the current text in the editor.
							return oEditor.GetXHTML();
						}
					}
				}
				else if (editorType=="tinymce") {
					return tinyMCE.getContent(id);
				}
				else if (editorType=="xinha") {
					//var ed = editors[id];
					var ed = fcHTMLControls[id].editor;
					if (typeof Xinha != "undefined") {
						return ed.getHTML();
					}
					return ed.value;
				}
				
			}
		}
		return "";
	}

	// get the scripts dynamically to include
	function fcGetIncludes(includes) {
		var len = includes.length;
		//var head = document.getElementsByTagName("head");
		// sometimes this doesn't work. possibly browser or sandbox issues. in those cases edit the html page and add scripts manually
		for (var i=0;i<len;i++) {
			var el = document.createElement("script");
			//el.onload = onload2;
			el.setAttribute("src",includes[i]);
			el.setAttribute("type","text/javascript");
			document.body.appendChild(el);
		}
	}

	// get reference to the flash movie	
	function fcGetMovieById(id) {
		if (navigator.appName.indexOf ("Microsoft") !=-1) {
			return window[id];
		} else {
			return window.document[id];
		}
	}
	
	// hide element by name
	// set height of content to 0px so the 
	// flash context menu appears in the right location
	// note: display none also fixes the issue but have not implemented yet
	function fcHide(id, hideOffscreen, offscreenOffset) {
		var el = fcGetElement(id);
		if (hideOffscreen) {
			//el.style.width = "0px";
			//el.style.height = "0px";
		}
		//el.style.visibility = "hidden";
		el.style.display = "none";
	}
	
	// move element to new location
	function fcMoveElementTo(el,x,y) {
		var movie = fcGetMovieById(el.movieId);
		if (fcRelativeMovie) {
			el.style.left = parseInt(x) + fcFindPosition(movie)[0] + "px";
			el.style.top = parseInt(y) + fcFindPosition(movie)[1] + "px";
		}
		else {
			el.style.left = parseInt(x) + "px";
			el.style.top = parseInt(y) + "px";
		}
	}
	
	// warn user if they or a link try to navigate them away from the page - used to prevent data loss / save changes
	function fcPromptOnUnload(prompt, message) {
		if (String(prompt) == "true") {
			// todo wrap existing function if it exists
			//fcExistingOnBeforeUnload = (window.onbeforeunload!=null) ? window.onbeforeunload : null;
			window.onbeforeunload = function (e) {
				//if (fcExistingOnBeforeUnload) {
				//	fcExistingOnBeforeUnload();
				//}
				//fcPurge(document.body);
				return message;
			};
		}
		else {
			window.onbeforeunload = function (e) {
				// see you later
				//if (fcExistingOnBeforeUnload) {
				//	fcExistingOnBeforeUnload();
				//}
			}
		}
	}
	
	// call before removing from DOM - necessary?
	// credit - http://javascript.crockford.com/memory/leak.html
	function fcPurge(d) {
		var a = d.attributes, i, l, n;
		if (a) {
			l = a.length;
			for (i = 0; i < l; i += 1) {
				n = a[i].name;
				if (typeof d[n] === 'function') {
					d[n] = null;
				}
			}
		}
		a = d.childNodes;
		if (a) {
			l = a.length;
			for (i = 0; i < l; i += 1) {
				fcPurge(d.childNodes[i]);
			}
		}
	}
	
	// forces redraw
	function fcRefresh(id) {
		var el = fcGetElement(id);
		el.style.cssText = el.style.cssText;
	}
	
	// remove and get memory back	
	function fcRemove(id) {
		//fcHide(id, true);
		var el = fcGetElement(id);
		var elParent = el.parentNode;
		fcPurge(el);
		fcHTMLControls[id] = null;
		elParent.removeChild(el);
	}
	
	// clips the html content
	function fcSetClip(id, top, right, bottom, left) {
		var el = fcGetElement(id);
		if (el!=null) {
			el.style.clip = "rect(" + top + " " + right + " " + bottom + " " + left  + ")";
		}
	}
    
	// set document title
    function fcSetDocumentTitle(title) {
        window.document.title = title;
    }
	
	// set HTML content
	function fcSetHTML(id, htmlText, elementType, editorType, chrome) {
		var el = fcGetElement(id);
		if (el!=null) {
		
			if (elementType =="division" && String(chrome)=="true") {
				var elContent = fcGetElement(id + "_content");
				elContent.innerHTML = htmlText;
			}
			else if (elementType =="division") {
				el.innerHTML = htmlText;
			}
			else if (elementType == "editor") {
				var oEditor;
				
				// add additional editor support here
				if (editorType == "fckeditor") {
					if ( typeof( FCKeditorAPI ) != 'undefined' ) {
						oEditor = FCKeditorAPI.GetInstance( id + "Editor" );
						if ( oEditor )	{
							// Set the text in the editor.
							oEditor.SetHTML( htmlText ) ;
						}
					}
				}
				else if (editorType=="tinymce") {
					var editor = tinyMCE.getInstanceById(id+"Editor");
					editor.setHTML(htmlText);
				}
				else if (editorType=="xinha") {
					//var ed = editors[id];
					var ed = fcHTMLControls[id].editor;
					if (typeof Xinha != "undefined") { 
						ed.setEditorContent(htmlText);
					}
					else {
						ed.value = htmlText;
					}
				}
			}
		}
	}
	
	// set position - should we use fcFindPosition here?
	function fcSetPosition(id,x,y) {
		var el = fcGetElement(id);
		var movie = fcGetMovieById(fcHTMLControls[id].movieId);
		if (fcHTMLControls[id].popUp == true ) { return }
		
		// LEFT
		if (x != undefined) {
			el.style.left = parseInt(x) + fcFindPosition(movie)[0] + "px";
		}
		// TOP
		if (y != undefined) {
			el.style.top = parseInt(y) + fcFindPosition(movie)[1] + "px";
		}
	}
	
	// set scroll policy of element
	function fcSetScrollPolicy(el, overflow) {
		if (overflow != "resize") {
			el.style.overflow = overflow;
		}
	}
	
	// save cpu when movie is at upper left corner of window
	function fcSetRelativeMovie(isRelative) {
		fcRelativeMovie = (String(isRelative)!="false") ? true : false;
	}
	
	// set scroll policy of element by id
	function fcSetScrollPolicyById(id, overflow) {
		var el = fcGetElement(id);
		
		// setting this to anything other than auto in ff fails it
		if (overflow != "resize") {
			el.style.overflow = overflow;
		}
	}
	
	// set size
	function fcSetSize(el,w,h) {
		
		if (String(w)!="undefined" && String(w)!="") {
			// if width is a percentage pass in the string as is
			if (String(w).indexOf("%")!=-1) {
				el.style.width = w;
			}
			else {
				el.style.width = parseInt(w) + "px";
			}
		}
		
		if (String(h)!="undefined" && String(h)!="") {
			if (String(h).indexOf("%")!=-1) {
				el.style.height = h;
			}
			else {
				el.style.height = parseInt(h) + "px"; 
			}
		}
	}
	
	// set size called by id
	function fcSetSizeByValue(id,w,h) {
		var el = fcGetElement(id);
		if (el.style.display=="none") { return; }
		if (fcHTMLControls[id].popUp == true ) { return }
		
		if (String(w)!="undefined" && String(w)!="") {
			// if width is a percentage pass in the string as is
			if (String(w).indexOf("%")!=-1) {
				el.style.width = w;
			}
			else {
				el.style.width = parseInt(w) + "px";
			}
		}
		
		if (String(h)!="undefined" && String(h)!="") {
			if (String(h).indexOf("%")!=-1) {
				el.style.height = h;
			}
			else {
				el.style.height = parseInt(h) + "px"; 
			}
		}
	}
	
	// set iframe location
	function fcSetSource(id,source) {
		var el = fcGetElement(id);
		el.src = source;
	}
	
	// show element by name
	// set display to block and we dont have to set size
	function fcShow(id, hideOffscreen, left, width, height) {
		var el = fcGetElement(id);
		//el.style.visibility = "visible";
		el.style.display = "block";
		if (hideOffscreen) {
			//el.style.width = parseInt(width) + "px";
			//el.style.height = parseInt(height) + "px";
		}
	}
	
	function fcFlexOnload(movieId) {
		// calling callback is unreliable - swf movie may not be loaded yet
		//movie.onLoadComplete();
		fcHTMLControls.pageLoaded = "true";
	}
	
	// end of line
