//used to mark form dirty and warn user to save first.
var o2c=0;
var o3c=new Array();//array holds flexi.form id's
o_info.guibusy = false;
o_info.linkbusy = false;
//debug flag for this file, to enable debugging to the olat.log set JavaScriptTracingController to level debug
o_info.debug = false;

function o_init() {
	try {
		// all init-on-new-page calls here
		//return opener window
		o_getOpenWin().o_afterserver();	
	} catch(e) {
		if (o_info.debug) o_log("error in o_init: "+showerror(e));
	}	
}

function b_initEmPxFactor() {
	// read px value for 1 em from hidden div
	o_info.emPxFactor = Ext.get('b_width_1em').getWidth();
	if (o_info.emPxFactor == 0 || o_info.emPxFactor == 'undefined') {
		o_info.emPxFactor = 12; // default value for all strange settings
		if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug('Could not read with of element b_width_1em, set o_info.emPxFactor to 12', "functions.js");
	}
}

function o_getOpenWin() {
	var w = top;
	try {
		if (w.opener && w.opener.o_info) {
			w = w.opener;
		}
	} catch (e) {}
	return w;
}

function o_beforeserver() {
//mal versuche mit Ext.onReady().. erst dann wieder clicks erlauben...
	o_info.linkbusy = true;
	setTimeout("showAjaxBusy()",1000);
	// execute iframe specific onunload code on the iframe
	if (window.suppressOlatOnUnloadOnce) {
		// don't call olatonunload this time, reset variable for next time
		window.suppressOlatOnUnloadOnce = false;
	} else if (window.olatonunload) {
		olatonunload();
	}
}

function o_afterserver() {
	o2c = 0;
	o_info.linkbusy = false;
	removeAjaxBusy();
}

function o2cl() {
	// alert("busy:"+o_info.linkbusy);
	if (o_info.linkbusy) {
		return false;
	} else {
		var doreq = (o2c==0 || confirm(o_info.dirty_form));
		if (doreq) o_beforeserver();
		return doreq;
	}
}

function o3cl(formId) {
	if (o_info.linkbusy) {
		return false;
	} else {
		//detect if another flexi form on the screen is dirty too
		var isRegistered = o3c1.indexOf(formId) > -1;
		var flexiformdirty = (isRegistered && o3c1.length > 1) || o3c1.length > 0;
		//check if no other flexi form is dirty
		//otherwise ask if changes should be discarded.
		var doreq = ( !flexiformdirty || confirm(o_info.dirty_form));
		if (doreq) o_beforeserver();
		return doreq;
	}
}

// on ajax poll complete
function o_onc(response) {
	var te = response.responseText;
	var r;
	eval("r="+te+";");
	//asynchronous! from polling
	o_ainvoke(r,false);
}

function o_allowNextClick() {
	o_info.linkbusy = false;
	removeAjaxBusy();
}

//remove busy after clicking a download link in non-ajax mode
//use LinkFactory.markDownloadLink(Link) to make a link call this method.
function removeBusyAfterDownload(e,target,options){
	o2c = 0;
	o_afterserver();
}


//thanks to http://blog.stevenlevithan.com/archives/faster-than-innerhtml to make firefox faster
/* This is much faster than using (el.innerHTML = value) when there are many
existing descendants, because in some browsers, innerHTML spends much longer
removing existing elements than it does creating new ones. */
function o_replaceHtml(el, html) {
	var oldEl = el;
	/*@cc_on // Pure innerHTML is slightly faster in IE
	oldEl.innerHTML = html;
	return oldEl;
	@*/
	var newEl = oldEl.cloneNode(false);
	newEl.innerHTML = html;
	oldEl.parentNode.replaceChild(newEl, oldEl);
	/* Since we just removed the old element from the DOM, return a reference
	to the new element, which can be used to restore variable references. */
	return newEl;
};

// b_AddOnDomReplacementFinishedCallback is used to add callback methods that are executed after
// the DOM replacement has occured. Note that when not in AJAX mode, those methods will not be 
// executed. Use this callback to execute some JS code to cleanup eventhandlers or alike
var b_onDomReplacementFinished_callbacks=new Array();//array holding js callback methods that should be executed after the next ajax call
function b_AddOnDomReplacementFinishedCallback(funct) {
	b_onDomReplacementFinished_callbacks.push(funct);
}

// main interpreter for ajax mode
var o_debug_trid = 0;
function o_ainvoke(r) {
	// commands
	var cmdcnt = r["cmdcnt"];
	if (cmdcnt > 0) {
		if (o_info.debug) { o_debug_trid++; }
		var cs = r["cmds"];
		for (var i=0; i<cmdcnt; i++) {
			var acmd = cs[i];
			var co = acmd["cmd"];
			var cda = acmd["cda"];
			var wid = acmd["w"];
			var wi = this.window; // for cross browser window: o_info.wins[wid]; 
			var out;
			if (wi) {
				switch (co) {
					case 2:  // redraw components command
						var cnt = cda["cc"];
						var ca = cda["cps"];
						for (var j=0;  j<cnt; j++) {
							var c1 = ca[j];
							var ciid = c1["cid"]; // component id
							var civis = c1["cidvis"];// component visibility
							var hfrag = c1["hfrag"]; // html fragment of component
							var jsol = c1["jsol"]; // javascript on load
							var hdr = c1["hdr"]; // header
							if (o_info.debug) o_log("c2: redraw: "+c1["cname"]+ " ("+ciid+") "+c1["hfragsize"]+" bytes, listener(s): "+c1["clisteners"]);
							var con = hfrag.stripScripts();
							var hdrco = hdr+"\n\n"+con;
							var inscripts = hfrag.extractScripts();
							var newc = wi.document.getElementById("o_c"+ciid);
							if (newc == null) {
								if (o_info.debug) o_logwarn("could not find comp with id: o_c"+ciid+",\nname: "+c1["cname"]+",\nlistener(s): "+c1["clisteners"]+",\n\nhtml:\n"+hfrag);
								if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Error in o_ainvoke(), could not find comp with id: o_c"+ciid+",\nname: "+c1["cname"]+",\nlistener(s): "+c1["clisteners"]+",\n\nhtml:\n"+hfrag, "functions.js");
							} else {
								if(civis){ // needed only for ie 6/7 bug where an empty div requires space on screen
									newc.style.display="";//reset?
								}else{
									newc.style.display="none";
								}
								//newc.innerHTML = hdrco;	= original, new one is faster: test
								newc = o_replaceHtml(newc, hdrco);
								if (inscripts!= "") {
									inscripts.each(
										function(val) { 
											// wrap with try/catch for saver execution
											var myval = 'try {' + val + '} catch (e) { ';
											if (o_info.debug) { // add webbrowser console log
												var s = "";
												inscripts.each(function(val) { s+= '\n:::'+val;});
												myval = myval + 'o_logerr("inscripts error:"+showerror(e)+" for: '+ escape(s) +'");';
											}
											if (B_AjaxLogger.isDebugEnabled()) { // add ajax logger
												var s = "";
												inscripts.each(function(val) { s+= '\n:::'+ val;});
												myval = myval + 'B_AjaxLogger.logDebug("inscripts error:"+showerror(e)+" for:'+ escape(s) +'", "functions.js");';
											}
											myval = myval + '}';
											// execute wrapped inscripts
											if (wi.ActiveXObject){ 
												wi.execScript(myval); 
											} else {
												//to reach global scope: timeout instead of wi.eval(val);
												wi.setTimeout(myval, 0);
											}
										} );
								}
								if (jsol != "") {
									// wrap with try/catch for saver execution
									var myjsol = 'try {' + jsol + '} catch (e) { ';
									if (o_info.debug) { // add webbrowser console log
										myjsol = myjsol + 'o_logerr("jsol2 error:"+showerror(e)+" while executing:::' + escape(jsol)+':::");';
									}
									if (B_AjaxLogger.isDebugEnabled()) { // add ajax logger
										myjsol = myjsol + 'B_AjaxLogger.logDebug("Error in o_ainvoke(), jsol2 error:"+showerror(e)+"while executing:::'+escape(jsol)+':::", "functions.js");';
									}
									myjsol = myjsol + '}';
									// execute wrapped jsol
									if (wi.ActiveXObject){ 
										wi.execScript(jsol);
									} else {
										wi.setTimeout(jsol,0);
									}
								}
								if (ishighlight) new Effect.Highlight(newc);
							}
						}
						break;
					case 3:  // createParentRedirectTo leads to a full page reload
						wi.o2c = 0;//??
						var rurl = cda["rurl"];
						wi.document.location.replace(rurl);
						break;
					case 5: // create redirect for external resource mapper
						wi.o2c = 0;//??
						var rurl = cda["rurl"];
						//in case of a mapper served media resource (xls,pdf etc.)
						wi.o_afterserver();
						wi.document.location.replace(rurl);//opens non-inline media resource
						break;
					case 6: // createPrepareClientCommand
						wi.o2c = 0;
						wi.o_afterserver();
						break;
					case 7: // JSCSS: handle dynamic insertion of js libs and dynamic insertion/deletion of css stylesheets
						// css remove, add, js add order should makes no big difference? except js calling/modifying css? 
						var loc = wi.document.location;
						var furlp = loc.protocol+"//"+loc.hostname; // e.g. http://my.server.com:8000
						if (loc.port != "" ) furlp += ":"+ loc.port; 
						// 1. css removes
						var cssrm = cda["cssrm"];
						for (j = 0; j<cssrm.length; j++) {
							var ce = cssrm[j];
							var id = ce["id"];
							var url = furlp + ce["url"];
							if (o_info.debug) o_log("c7: rm css: id:"+id+" ,url:'"+url+"'");
							o_removeStyle(url,id);
						}
						// css adds
						var cssadd = cda["cssadd"];
						for (k = 0; k<cssadd.length; k++) {
							var ce = cssadd[k];
							var id = ce["id"];
							var url = furlp + ce["url"];
							if (o_info.debug) o_log("c7: add css: id:"+id+" ,url:'"+url+"'");
							o_applyStyle(url, id);
						}
						
						// js lib adds
						var jsadd = cda["jsadd"];
						for (l=0; l<jsadd.length; l++) {
							var ce = jsadd[l];
							var cont = ce["cont"];
							if (o_info.debug) o_log("c7: add js: "+ce["path"]+" cont len:" + cont.length+" start with:"+escape(cont.substr(0,20)));
							
							// wrap with try/catch for saver execution
							var mycon = 'try {' + cont + '} catch (e) { ';
							if (o_info.debug) { // add webbrowser console log
								mycon = mycon + 'o_logerr("js lib:"+showerror(e)+" for: '+escape(cont)+'");';
							}
							if (B_AjaxLogger.isDebugEnabled()) { // add ajax logger
								mycon = mycon + 'B_AjaxLogger.logDebug("Error in o_ainvoke(), js lib:"+showerror(e)+" for: '+escape(cont)+ '", "functions.js");';
							}
							mycon = mycon + '}';
							// execute wrapped con
							if (wi.ActiveXObject) { 
								wi.execScript(mycon);
							} else {
								//to reach global scope: timeout instead of wi.eval(val);
								wi.setTimeout(mycon,0);
							}
						}
						break;	
					default:
						if (o_info.debug) o_log("?: unknown command "+co); 
						if (B_AjaxLogger.isDebugEnabled()) 	B_AjaxLogger.logDebug("Error in o_ainvoke(), ?: unknown command "+co, "functions.js");
						break;
				}		
			} else {
				if (o_info.debug) o_log ("could not find window??");
				if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Error in o_ainvoke(), could not find window??", "functions.js");
			}		
		}
		// execute onDomReplacementFinished callback functions
		var mycounter = 0;
		while ( b_onDomReplacementFinished_callbacks.length > 0 ) { 
			mycounter++;
			if (mycounter > 50) {
				if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Stopped executing DOM replacement callback functions - to many functions::" + b_onDomReplacementFinished_callbacks.length, "functions.js");
				break; // emergency break
			}
			var func = b_onDomReplacementFinished_callbacks.shift();
			if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Executing DOM replacement callback function #" + mycounter + " with timeout funct::" + func, "functions.js");
			// don't use execScript here - must be executed outside this function scope so that dom replacement elements are available
			this.window.setTimeout(func,0);
		}
	}
}

function showAjaxBusy() {
	// release o_info.linkbusy only after a successful server response 
	// - otherwhise the response gets overriden by next request
	if (o_info.linkbusy) {
		// try/catch because can fail in full page refresh situation when called before DOM is ready
		try {
			$('b_ajax_busy').addClassName('b_ajax_busy');
			$('b_body').addClassName('b_ajax_busy');
		} catch (e) {}
	}
}

function removeAjaxBusy() {
	// try/catch because can fail in full page refresh situation when called before page DOM is ready
	try { 
		$('b_ajax_busy').removeClassName('b_ajax_busy');
		$('b_body').removeClassName('b_ajax_busy');
	} catch (e) {}
}

//safari destroys new added links in htmleditor see: http://bugs.olat.org/jira/browse/OLAT-3198
var htmlEditorEnabled = (Prototype.Browser.IE || Prototype.Browser.Gecko);
var scormPlayerEnabled = (Prototype.Browser.IE || Prototype.Browser.Gecko || Prototype.Browser.WebKit);

function setFormDirty(formId) {
	// sets dirty form content flag to true and renders the submit button
	// of the form with given dom id as dirty.
	// (fg) 
	o2c=1;
	// fetch the form and the forms submit button is identified via the olat 
	// form submit name
	var myForm = document.getElementById(formId);
	//TODO:gs:a why not directly accessing the submit button by an id. name="olat_fosm" send additional parameter which is unused. OLAT-1363
	var mySubmit = myForm.olat_fosm_0;
	if(mySubmit == null){
		mySubmit = myForm.olat_fosm;
	}
	// set dirty css class
	if(mySubmit) mySubmit.className ="b_button b_button_dirty";
}


//Pop-up window for context-sensitive help
function contextHelpWindow(URI) {
helpWindow = window.open(URI, "HelpWindow", "height=760, width=940, left=0, top=0, location=no, menubar=no, resizable=yes, scrollbars=yes, toolbar=no");
helpWindow.focus();
}

//TODO:b replace by EXT.js call
function o_hideDiv(divId) {
	// helper function: remove element from screen using css
	// (fg)
	try {
		var divElem = $(divId);
		if (divElem != null) {
			divElem.style.display = "none";
		}
	} catch (e) {
		// fails when divId is not in DOM
	}
}

//TODO:b replace by EXT.js call
function o_showDiv(divId) {
	// helper function: show element on screen using css
	// (fg)
	try {
		var divElem = $(divId);
		if (divElem != null) {
			divElem.style.display = "block";
		}
	} catch (e) {
		// fails when divId is not in DOM
	}
}

//TODO: for 5.3 add popup capability to link and table
function o_openPopUp(url, windowname, width, height, menubar) {
	// generic window popup function
	attributes = "height=" + height + ", width=" + width + ", resizable=yes, scrollbars=yes, left=100, top=100, ";
	if (menubar) {
		attributes += "location=yes, menubar=yes, status=yes, toolbar=yes";
	} else {
		attributes += "location=no, menubar=no, status=no, toolbar=no";
	}
	var win = window.open(url, windowname, attributes);
	win.focus();
}

function b_togglebox(domid, toggler) {
	// toggle the domid element and switch the toggler classes
	new Effect.toggle(domid, 'slide', {duration: 0.5}); 
	if(toggler.hasClassName('b_togglebox_closed')) {
		toggler.removeClassName('b_togglebox_closed');
		toggler.addClassName('b_togglebox_opened');
	} else {
		toggler.removeClassName('b_togglebox_opened');
		toggler.addClassName('b_togglebox_closed');
	}
}

function b_handleFileUploadFormChange(fileInputElement, fakeInputElement, saveButton) {
	// file upload forms are rendered transparent and have a fake input field that is rendered.
	// on change events of the real input field this method is triggered to display the file 
	// path in the fake input field. See the code for more info on this
	var fileName = fileInputElement.value;
	// remove unix path
	slashPos = fileName.lastIndexOf('/');
	if (slashPos != -1) {
		fileName=fileName.substring(slashPos + 1); 
	}
	// remove windows path
	slashPos = fileName.lastIndexOf('\\');	
	if (slashPos != -1) {
		fileName=fileName.substring(slashPos + 1); 
	}
	fakeInputElement.value=fileName;
	// mark save button as dirty
	if (saveButton) {
		saveButton.className='b_button b_button_dirty'
	}
	// set focus to next element if available
	var elements = fileInputElement.form.elements;
	for (i=0; i < elements.length; i++) {
		var elem = elements[i];
		if (elem.name == fakeInputElement.name && i+1 < elements.length) {
			elements[i+1].focus();
		}
	}
}

// goto node must be in global scope to support content that has been opened in a new window 
// with the clone controller - real implementation is moved to course run scope o_activateCourseNode()
function gotonode(nodeid) {
	try {
		// check if o_activateCourseNode method is available in this window
		if (typeof o_activateCourseNode != 'undefined') {
			o_activateCourseNode(nodeid);
		} else {
			// must be content opened using the clone controller - search in opener window
			if (opener && typeof opener.o_activateCourseNode != 'undefined') {
			  opener.o_activateCourseNode(nodeid);
			} else {
				if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Error in gotonode(), could not find main window", "functions.js");
			}			
		}
	} catch (e) {
	 alert(e);
		if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Error in gotonode()::" + e.message, "functions.js");
	}
}


//TODO:: discuss with HJZ how calendar and course should be designed
function o_openUriInMainWindow(uri) {
	// get the "olatmain" window
	var w = top;
	try {
		if (w.opener && w.opener.o_info) {
		  w = w.opener;
		}
	} catch (e) {}
	
	w.focus();
	w.location.replace(uri);
}

function b_viewportHeight() {
	// based on prototype library
	var prototypeViewPortHight = document.viewport.getHeight()
	if (prototypeViewPortHight > 0) {
		return prototypeViewPortHight;
	} else {
		return 600; // fallback
	}
}

function b_getMainColumnsMaxHeight() {
	// calculate the height of the inner content area that can be used for 
	// displaying content without using scrollbars. 
	// Depends on prototype library
	// (fg)
	var col1Height = 0
	var col1 = $('b_col1_content');
	if (col1 != 'undefined' && col1 != null) col1Height = col1.getHeight();

	var col2Height = 0
	var col2 = $('b_col2_content');
	if (col2 != 'undefined' && col2 != null) col2Height = col2.getHeight();

	var col3Height = 0
	var col3 = $('b_col3_content');
	if (col3 != 'undefined' && col3 != null) col3Height = col3.getHeight();

	var mainInnerHeight = (col1Height > col2Height ? col1Height : col2Height);
	mainInnerHeight = (mainInnerHeight > col3Height ? mainInnerHeight : col3Height);

	if (mainInnerHeight > 0) {
		return mainInnerHeight;
	} 
	// fallback, try to get height of main container
	var mainHeight = 0
	var main = $('b_main');
	if (main != 'undefined' && main != null) mainHeight = main.getHeight();
	if (main > 0) {
		return main;
	} 
	// fallback to viewport height	
	return b_viewportHeight();
}
  
function b_resizeIframeToMainMaxHeight(iframeId) {
	// adjust the given iframe to use as much height as possible
	// (fg)
	var theIframe = $(iframeId);
	if (theIframe != 'undefined' && theIframe != null) {
		var colsHeight = b_getMainColumnsMaxHeight();
		
		var potentialHeight = b_viewportHeight() - 100;// remove some padding etc.
		var elem = $('b_header');
		if (elem != 'undefined' && elem != null) potentialHeight = potentialHeight - elem.getHeight();
		elem = $('b_nav');
		if (elem != 'undefined' && elem != null) potentialHeight = potentialHeight - elem.getHeight();
		elem = $('b_footer');
		if (elem != 'undefined' && elem != null) potentialHeight = potentialHeight - elem.getHeight();
		// resize now		
		theIframe.style.height = (potentialHeight > colsHeight ? potentialHeight : colsHeight) + "px";
	}
}
// for gui debug mode
var o_debu_oldcn, o_debu_oldtt;

function o_debu_show(cn, tt) {
	if (o_debu_oldcn) o_debu_hide(o_debu_oldcn, o_debu_oldtt);
	cn.style.border='3px solid #00F'; 
	cn.style.margin='0px';
	cn.style.background='#FCFCB8';
	Element.show(tt);
	o_debu_oldtt = tt;
	o_debu_oldcn = cn;
}

function o_debu_hide(cn, tt) {
	Element.hide(tt);
	cn.style.border='1px dotted black'; 
	cn.style.margin='2px';
	cn.style.background='';
}

function o_dbg_mark(elid) {
	var el = $(elid);
	if (el) { 
		//el.style.margin='0px';
		el.style.background='#FCFCB8';
		el.style.border='3px solid #00F'; 
	}
}

function o_dbg_unmark(elid) {
	var el = $(elid);
	if (el) {
		//el.style.margin='0px';
		el.style.border=''; 
		el.style.background='';
	}
}

function o_clearConsole() {
 o_log_all="";
 o_log(null);
}

var o_log_all = "";
function o_log(str) {
	if (str) {	
		o_log_all = "\n"+o_debug_trid+"> "+str + o_log_all;
		o_log_all = o_log_all.substr(0,4000);
	}
	var logc = $("o_debug_cons");
	if (logc) {
		if (o_log_all.length == 4000) o_log_all = o_log_all +"\n... (stripped: to long)... ";
		logc.value = o_log_all;
	}
	if(!Prototype.Browser.IE && console != 'undefined'){
		//firebug log window
		console.log(str);
	}
}

function o_logerr(str) {
	o_log("ERROR:"+str);
}

function o_logwarn(str) {
	o_log("WARN:"+str);
}


function showerror(e) {
	var r = "";
    for (var p in e) r += p + ": " + e[p] + "\n";
    return "error detail:\n"+r;
}


// dynamic js / css handling
function o_applyStyle(styleurl, linkid) {
	if(document.createStyleSheet) { // IE
		// double check: server side should do so, but to make sure that we don't have duplicate styles
		var sheets = document.styleSheets;
		var cnt = 0;
		for (i = 0; i < sheets.length; i++) {
			var sh = sheets[i];
			var h = sh.href; 
			if (h == styleurl) {
				cnt++;
				if (sh.disabled) {
					// enable a previously disabled stylesheet (ie cannot remove sheets? -> we had to disable them)
					sh.disabled = false;
					return;
				} else {
					if (o_info.debug) o_logwarn("style: "+styleurl+" already in document and not disabled! (duplicate add)");
					return;
				}
			}
		}
		if (cnt > 1 && o_info.debug) o_logwarn("apply styles: num of stylesheets found was not 0 or 1:"+cnt);
		
		// H: stylesheet not yet inserted -> insert				
		var mystyle = document.createStyleSheet(styleurl);
	} else { // mozilla
		// double check: first try to remove the <link rel="stylesheet"...> tag, using the id.
		var el = $(linkid);
		if (el) {
			if (o_info.debug) o_logwarn("stylesheet already found in doc when trying to add:"+styleurl+", with id "+linkid);
			return;
		}
		// create the new stylesheet and convince the browser to load the url using @import with protocol 'data'
		var newSt=document.createElement('link');
		newSt.rel='stylesheet';
		newSt.id = linkid;
		var styles = "@import url('"+styleurl+"');";
		newSt.href='data:text/css,'+escape(styles);
		document.getElementsByTagName("head")[0].appendChild(newSt);
	}
}

function o_removeStyle(styleurl, linkid) {
	if(document.createStyleSheet) { // IE
		var sheets = document.styleSheets;
		var cnt = 0;
		for (i = 0; i < sheets.length; i++) {
			var h = sheets[i].href; 
			if (h == styleurl) {
				cnt++;
				if (!sheets[i].disabled) {
				//alert("removing style for ie");
					sheets[i].disabled = true; // = null;
				} else {
					if (o_info.debug) o_logwarn("stylesheet: when removing: matching url, but already disabled! url:"+h);
				}
				// return;
			}
		}
		if (cnt != 1 && o_info.debug) o_logwarn("stylesheet: when removeing: num of stylesheets found was not 1:"+cnt);
		
	} else { // mozilla
		var el = $(linkid);
		if (el) {
			document.getElementsByTagName("head")[0].removeChild(el);
			return;
		} else {
			if (o_info.debug) o_logwarn("no link with id found to remove, id:"+linkid+", url "+styleurl);
		}
	}
}

// Each flexible.form item with an javascript 'on...' configured calls this fn.
// It is called at least if a flexible.form is submitted.
// It submits the component id as hidden parameters. This specifies which 
// form item should be dispatched by the flexible.form container. A second
// parameter submitted is the action value triggering the submit.
// A 'submit' is not the same as 'submit and validate'. if the form should validate
// is defined by the triggered component.
function o_ffEvent (formNam, dispIdField, dispId, eventIdField, eventInt){
	//set hidden fields and submit form
	var elem = document.getElementById(dispIdField);
	elem.value=dispId;
	elem = document.getElementById(eventIdField);
	elem.value=eventInt;
	// manually execute onsubmit method - calling submit itself does not trigger onsubmit event!
	if (document.forms[formNam].onsubmit()) {
		document.forms[formNam].submit();
	}
}

//
// param formId a String with flexi form id
function setFlexiFormDirtyByListener(e,target,options){
	setFlexiFormDirty(options.formId);
}

function setFlexiFormDirty(formId){
	//if old form on same page with flexi.form
	o2c=1;
	
	var isRegistered = o3c.indexOf(formId) > -1;
	if(!isRegistered){
		o3c.push(formId);
	}
	var myForm = document.getElementById(formId);
	var submitButton = document.getElementById(myForm["FlexiSubmit"]);
	if(submitButton != null){
		//not all forms must have a submit element! 
		submitButton.className ="b_button b_button_dirty";
	}
}

//
//
function o_ffRegisterSubmit(formId, submElmId){
	var myForm = document.getElementById(formId);
	myForm["FlexiSubmit"] = submElmId;
}
/*
* renders an info msg that slides from top into the window
* and hides automatically
*/
function showInfoBox(title, format){
	var msgCt;
	function createBox(t, s){
	        return ['<div class="msg">',
	                '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
	                '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc"><div class="b_msg_info_content b_msg_info_winicon"><h3>', t, '</h3>', s, '<br/><br/></div></div></div></div>',
	                '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
	                '</div>'].join('');
	}
	if(!msgCt){
    	msgCt = Ext.DomHelper.insertFirst($('b_page'), {id:'b_msg-div'}, true);
    }
    // line below collides with css styling - use b_msg-div for positioning and styling
	// msgCt.alignTo(document, 't-t');
    var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1));
    var m = Ext.DomHelper.append(msgCt, {html:createBox(title, s)}, true);
    var time = 4;
    if (s.length > 70) time=6;
    if (s.length > 150) time=8;
    m.slideIn('t').pause(time).ghost("t", {remove:true});
    // Visually remove message box immediately when user clicks on it
    // The ghost event from above is triggered anyway. 
    m.on("click", function() {$('b_msg-div').style.display="none"});
}
/*
* renders an message box which the user has to click away
* The last parameter buttonCallback is optional. if a callback js 
* function is given it will be execute when the user clicks ok or closes the message box
*/
function showMessageBox(type, title, message, buttonCallback){
	if(type == 'info'){
		showInfoBox(title, message)
	} else {
		Ext.MessageBox.show({
			title: title,
			msg: message,
			icon: 'b_msg_'+type+'_winicon',
			minWidth: 300,
			buttons: Ext.MessageBox.OK,
			fn: buttonCallback
		});
	}
}


/*
* print command, prints iframes when available
*/
function b_doPrint() {
	// When we have an iframe, issue print command on iframe directly
	var iframes =  $$('div.b_iframe_wrapper iframe');
	if (iframes.length > 0) {
		try {
			var iframe = iframes[0];
			frames[iframe.name].focus();
			frames[iframe.name].print();
			return;
		} catch (e) {
			// When iframe content renames the window, the method above does not work.
			// We use best guess code to find the target iframe in the window frames list
			for (i=0; frames.length > i; i++) {
				iframe = frames[i];
				if (iframe.name == 'oaa0') continue; // skip ajax iframe
				if (iframe.getAttribute('class') == 'ext-shim') continue; // skip ext shim iframe
				// Buest guess is that this is our renamed target iframe			
				if (iframe.name != '') {
					try {
						frames[iframe.name].focus();
						frames[iframe.name].print();				
					} catch (e) {
						// fallback to window print
						window.print()
					}
					return;
				}
			}		
			// fallback to window print
			window.print()
		}
	} else {
		// no iframes found, print window
		window.print()
	}
}


/*
 * Attach event listeners to enable inline translation tool hover links
 */ 
function b_attach_i18n_inline_editing() {
	// Add hover handler to display inline edit links
	Ext.select('span.b_translation_i18nitem').hover(function(){	
		Ext.get(this.firstChild).show();
		//if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Entered i18nitem::" + this.firstChild, "functions.js:b_attach_i18n_inline_editing()");
	},function(){
		Ext.select('a.b_translation_i18nitem_launcher').hide();
		//if (B_AjaxLogger.isDebugEnabled()) B_AjaxLogger.logDebug("Leaving i18nitem::" + this, "functions.js:b_attach_i18n_inline_editing()");
	});
	// Add highlight effect on link to show which element is affected by this link
	Ext.select('a.b_translation_i18nitem_launcher').hover(function(){	
		Ext.get(this).findParent('span.b_translation_i18nitem').highlight();
	},function(){});
	// Add to on ajax ready callback for next execution
	b_AddOnDomReplacementFinishedCallback(b_attach_i18n_inline_editing);
}
