var slides;
var fades = new Array();
var fadeDuration = 1000;
var MODULES_URL = '/includes/modules';
var BASE_IMAGES = MODULES_URL + '/base/images';
var DELIMETER = "_&_";
var mouseX, mouseY;
var headerToolbarButtons = Array();
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};

BrowserDetect.init();

function add_filtered_multiselect_item(listID, itemID, text, variableName, sortableList) {
	
	var newID = variableName+"_"+itemID;
	if (document.getElementById(newID) == undefined) {
		var parent = $('#' + listID);
		
		var newLI = $('<li lid="' + itemID + '" id="' + newID + '"></li>');
		newLI.html('<a href=\'javascript:delete_filtered_multiselect_item("'+listID+'", "'+itemID+'", "'+variableName+'")\'>' +
			(sortableList ? '<img src="' + BASE_IMAGES + '/admin/icons-16/delete.png" alt="Delete Item" /></a> ' : '') +
			(sortableList ? '<img class="drag" src="' + BASE_IMAGES + '/admin/drag.gif" alt="Drag" />' : '')+
			'<span>'+text+'</span>'	+
			(!sortableList ? '</a>' : ''));
			
		parent.append(newLI);

		//need to add to javascript Sortables object
		if (sortableList) {
			sortableList = parent.sortable({
				"stop": function(){
					var values = get_list_order(listID, "lid");
					$('#' + variableName).val(values.join(DELIMITER));
				}
			 });
		}
		
		//also mark as "selected" in hidden field
		var values = get_list_order(listID, "lid");

		$('#' + variableName).val(values.join(DELIMETER));
	}
}

function add_header_toolbar_buttons() {
	var headerToolbar = $('#headerToolbarButtons');
	if (headerToolbar) {
		$.each(headerToolbarButtons, function(i, buttonHtml) {
			//console.log(buttonHtml);
			headerToolbar.append(buttonHtml);
		});
	}
}

function add_orderable_list_item(listID, itemID, text, variableName, sortableList) {
	var isDivider = false,
		newId,
		parent,
		newLI,
		lidVal;
	
	if (itemID == -1){ //is a divider?
		isDivider = true;
		itemID = - Math.floor(Math.random()*1000+1);
	}
	
	newID = variableName+"_"+itemID;
	if (document.getElementById(newID) == undefined || isDivider == true) { //allow multiple dividers
		parent = $('#' + listID);
		lidVal = (isDivider ? -1 : itemID);
		
		newLI = $('<li id="' + newID + '" lid="' + lidVal + '"></li>');

		newLI.html('<a href=\'javascript:delete_orderable_list_item("' + listID + '", "' + itemID + '", "' + variableName + '");\'>'
			+ '<img src="' + BASE_IMAGES + '/admin/icons-16/delete.png" alt="Delete Item" /></a> '
			+ '<img class="drag" src="' + BASE_IMAGES + '/admin/drag.gif" alt="Drag" />'
			+ '<span>' + text + '</span>');
			
		parent.append($(newLI));

		var set_order = function(){
			var values = get_list_order(listID, "lid");
			$('#' + variableName).val(values.join(DELIMETER));
		};

		//need to add to javascript Sortables object
		if (sortableList) {
			//sortableList = new Sortables($(listID), {
			//	onComplete: set_order
			// });
			if (parent.hasClass('ui-sortable')) {
				parent.sortable('refresh');
			} else {
				parent.sortable({'update': set_order});
			}
		}
		//also mark as "selected" in hidden field
		set_order();
	}
	//else, msg "They're already in the list."
}

function addIEFavorite(bookmarkID, url, who, txt) {
	var browserName = navigator.appName;
	var browserVersion = parseInt(navigator.appVersion);

	if ((browserName == "Microsoft Internet Explorer") && (browserVersion >= 4)) {
		window.external.AddFavorite(url, who);
	} else {
		alert("Use CTRL-D to bookmark in Firefox.");
	}
}

function change_class(){
	args = change_class.arguments;
	for(i = 0; i < args.length; i += 2){
		if(document.getElementById(args[i])){
			document.getElementById(args[i]).className = args[i + 1];
		}
	}
}

function create_multi_selects(){
	var elements = $ES("ul", "body");
	var multiSelects = elements.filterByClass("multiSelect");
	
	for(var i = 0; i < multiSelects.length; i++){	
		var id = multiSelects[i].id
		var pos = id.indexOf("ID");
		var name = id.substr(0, pos);
		
		eval("var MSL_" + name + " = new MultiSelectList('MSL_" + name + "');");
	}
}

function create_slides(){
	var slideElements = $("a.slideVertical"),
		slides = new Array();
	
	slideElements.each(function(i, el) {
		// store the target
		var target = el.target,
			targetEl = $('#' + target),
			hideFirst;
		
		if (targetEl.length) {
			hideFirst = ((el.className.indexOf("hideFirst") > -1) || el.className.indexOf("hideNow") > -1 ? true : false);
			// set up the slide on the target id
			slides[i] = new SlideWrapper(el.id, target, hideFirst, "vertical", false);
	
			targetEl.css('display', "");
		}
	});
	
}

function delete_filtered_multiselect_item(listID, itemID, variableName) {
	var deleteID = variableName+"_"+itemID,
		parent = $('#' + listID),
		deleteEl = $('#' + deleteID);
	if (deleteEl.length === 0) {
		alert ("Item '" + deleteID + "' is not defined.");
	} else {
		deleteEl.hide().remove();
		var orderedList = get_list_order(listID, "lid");
		$('#' + variableName).val(orderedList.join(DELIMETER));
	}
}

function delete_orderable_list_item(listID, itemID, variableName) {
	var deleteID = variableName+"_"+itemID,
		parent = $('#' + listID),
		deleteEl = $('#' + deleteID);

	//remove item from list and refresh hidden field with new order
	if (deleteEl.length === 0) {
		alert ("Item '" + deleteID + "' is not defined.");
	} else {
		deleteEl.hide().remove();
		var orderedList = get_list_order(listID, "lid");
		$('#' + variableName).val(orderedList.join(DELIMETER));
	}
}

function escape_values(string){
	var str = "";
	var parts = string.split("&");
	for(var i = 0; i < parts.length; i++){
		var newparts = parts[i].split("=");
		if(i != 0){
			str += "&";
		}
		str += newparts[0] + "=" + escape(newparts[1]);
	}
	return str;
}

function evaluate_js(response){
	//alert("evaluate_js");
	// now find the scripts that are needed
	var startTag = "<script type='text/javascript'>";
	var endTag = "</script>";
	
	var pos = response.indexOf(startTag);

	while(pos > -1){
		var endPos = response.indexOf(endTag, pos + 1);
		if(endPos > -1){
			var start = pos + startTag.length;
			var js = response.substr(start, endPos - start);
			//alert(js)
			eval(js);
			pos = response.indexOf(startTag, endPos);
		}
	}
}

function evaluate_js_headers(response){
	var startTag = "<script type='text/javascript' src='";
	var endTag = "' />";
	
	var pos = response.indexOf(startTag);

	while(pos > -1){
		var endPos = response.indexOf(endTag, pos + 1);
		if(endPos > -1){
			var start = pos + startTag.length;
			var jsFile = response.substr(start, endPos - start);
			alert(jsFile);
			var script = document.createElement('script');
			script.type = 'text/javascript';
			script.src = jsFile;
			document.getElementsByTagName('head')[0].appendChild(script);
			pos = response.indexOf(startTag, endPos);
		}
	}
}

function event_mouse_move(){
	if (BrowserDetect.browser != "Explorer") document.captureEvents(Event.MOUSEMOVE);
	// Set-up to use getMouseXY function onMouseMove
	document.onmousemove = get_mouse_xy;
}

function fade_in(id){
	if($defined($(id)) && ($(id).style.display == "" || $(id).style.display == "none")){
		$(id).style.display = "block";
	}
	fade_to(id, 0, 100);
}

function fade_to(id, start, end){
	if($defined($(id))){
		// if slide isn't already defined, define it
		if(!$defined(fades[id])){
			fades[id] = new Fx.Style(id, 'opacity', {duration:fadeDuration});
		}
		fades[id].start(start, end);
	}
}

function fade_out(id){
	fade_to(id, 100, 0);
}

function filter_adminList(module, id, queryStr){
//default params
	if (typeof(module) == 'undefined' || typeof(id) == 'undefined')
		return 0;
	base_ajax(id, MODULES_URL + "/"+module+"/controllers/adminList.php", "justListItems=true&"+queryStr);
}

function filter_search(form, id){
	var queryStr = form.toQueryString();
	
	base_ajax(id, form.redirectURL.value, "mode=search&" + queryStr, "evaluate_js(response);");
	return false;
}

function get_cookie(cookiename){
	var allcookies = document.cookie;
	var pos = allcookies.indexOf(cookiename + "=");
	if(pos != -1){
		var start = pos + (cookiename.length + 1);
		var end = allcookies.indexOf(";", start);
		if(end == -1){ end = allcookies.length; }
		var value = allcookies.substring(start, end);
		value = unescape(value);
		return value;
		
	}else{
		return false;
	}
}

function get_cookie_filter(module) {
	return get_cookie('cms_'+module+'_filter'); 
}

function get_list_order(theList, attributeName){
	var ar = [];
	var children = $('#' + theList).children();
	// clear the styles first
	children.each(function() {
		if(this.nodeName == "LI"){	// make sure it is a list tag
			if(this.getAttribute(attributeName) != null){
				ar.push(this.getAttribute(attributeName));
			}
		}
	});
	return ar;	// return the array with the order
}

function get_mouse_xy(e) {
  if (BrowserDetect.browser == "Explorer") { // grab the x-y pos.s if browser is IE
    mouseX = event.clientX + document.body.scrollLeft
    mouseY = event.clientY + document.body.scrollTop
  } else {  // grab the x-y pos.s if browser is NS
    mouseX = e.pageX
    mouseY = e.pageY
  }  
  // catch possible negative values in NS4
  if (mouseX < 0){mouseX = 0}
  if (mouseY < 0){mouseY = 0}  

  return true
}

function get_selection_parts(area){

	if(document.selection && !(BrowserDetect.browser == "Explorer" && (BrowserDetect.version == "6" || BrowserDetect.version == "7"))){ // The current selection 
		var range = document.selection.createRange(); 
		// We'll use this as a 'dummy' 
		var stored_range = range.duplicate(); 
		// Select all text 
		stored_range.moveToElementText(area); 
		// Now move 'dummy' end point to end point of original range
		stored_range.setEndPoint( 'EndToEnd', range ); 
		// Now we can calculate start and end points 
		area.selectionStart = stored_range.text.length - range.text.length; 
		area.selectionEnd = area.selectionStart + range.text.length;
	}
	
	
	var s = area.value.substr(0, area.selectionStart);
	var sel = area.value.substr(area.selectionStart, area.selectionEnd - area.selectionStart);
	var e = area.value.substr(area.selectionEnd);
	
	if(sel == "" || (BrowserDetect.browser == "Explorer" && (BrowserDetect.version == "6" || BrowserDetect.version == "7"))){
		return false;
	}else{
		return {start : s, selected : sel, end : e};
	}	
}

// creates and manages an html select form element, from a
function MultiSelectList(id){
	var parts = id.split("_");
	this.listID = id;
	this.variableName = parts[1];
	//alert(this.listID + "ID");
	//alert(this.variableName + "ID");
	this.list = $('#' + this.variableName + "ID");
	this.children = this.list.children();
	this.listItems = new Array();
	this.select;
	this.selectedList = new Array();
	
	this.clear_list = function(){
		
		for(i = 0; i < this.selectedList.length; i++){

			// reset the css class
			this.highlight(this.selectedList[i], false);
			
			// update the select form field
			this.form_select(this.selectedList[i], false);
		}
		
		this.selectedList = new Array();
	};
	
	this.click = function(index){
		// if the item is already selected
		var position = this.is_selected(index);
		//alert(this.selectedList.join(","));
		if(position != -1){
			//alert("removing: length=" + this.selectedList.length);
			//alert("this.selectedList.splice(" + position + ",1);")
			
			// take the item out of the array
			this.selectedList.splice(position,1);
			// reset the css class
			this.highlight(index, false);
			// update the select form field
			this.form_select(index, false);
		}else{	// if new
			//alert("adding: length=" + this.selectedList.length);
			// add to the selected list
			this.selectedList.push(index);
			// highlight it
			this.highlight(index, true);
			// select the form field option
			this.form_select(index, true);
		}
	};
	
	this.form_select = function(index, selected){
		//alert("msoption-" + this.listID + "-" + index);
		//alert($("msoption-" + this.listID + "-" + index).selected);
		//$("msoption-" + this.listID + "-" + index).selected = selected;
		if (selected) {
			this.list.append($("<input type='hidden' name='" + this.variableName + '[' + index + "]' value='" + $("#msli-" + this.listID + "-" + index).attr("lid") + "' id='msoption-" + this.listID + "-" + index + "' />"));
		} else {
			$("#msoption-" + this.listID + "-" + index).remove();
		}
		//alert($("msoption-" + this.listID + "-" + index).selected);
	};
	
	this.highlight = function(index, color){
		var elem = $("#msli-" + this.listID + "-" + index);
		if(color == true){
			elem.attr('class', "highlight");
		}else{
			elem.attr('class', "normal");
		}
	};
	
	this.init = function(){
		// loop through the list and read the values
		for(i = 0; i < this.children.length; i++){
			this.add(this.children[i]);
		}
		// show all the ones that are already selected
		this.show_all_selected();
	};
	
	//Note that this enables dynamic adding of items to an MSL
	this.add = function(node) {
		var ret = false;
		if(node.nodeName == "LI"){	// make sure it is a list tag
			// add the list item
			var currentIndex = this.listItems.length;
			this.listItems.push(node);

			if (node.getAttribute("disabled") != 'disabled'){ //added by RJS 12/8/08 
			
				this._add_li(node, currentIndex);
				ret = currentIndex;

			}
		}
		return ret;
	};
	
	this._add_li = function(li, currentIndex) {
		
		// set the id of the list item for later reference
		li.setAttribute("id", "msli-" + this.listID + "-" + currentIndex);
		
		// create the link for the list item, and wrap it with a link
		var link = document.createElement('a');
		link.setAttribute("href", "javascript:" + this.listID + ".click(" + currentIndex + ");");
		
		// store the contents of the list item
		var nodeText = li.firstChild.nodeValue;
		var linkText = document.createTextNode(nodeText);
		link.appendChild(linkText);
		
		// remove the plain text, add new text with link wrapper
		li.removeChild(li.firstChild);
		li.appendChild(link);
		
		// check if the value is selected
		if(li.getAttribute("selected") != null){
			// add to the selected list
			this.selectedList.push(currentIndex);
			
		}
		
		return currentIndex;
	
	};
	
	this.is_selected = function(val){
		for(var i = 0; i < this.selectedList.length; i++){
			if(val == this.selectedList[i]){
				return i;
			}
		}
		return -1;
	};
	
	this.join = function(){
		var str = "";
		for(var i = 0; i < this.selectedList.length; i++){
			str += this.listItems[this.selectedList[i]].getAttribute("lid");
			if(i + 1 < this.selectedList.length){
				str += ",";
			}
		}
		return str;
	};
	
	this.show_all_selected = function(){
		
		for(var i = 0; i < this.selectedList.length; i++){
			// highlight it
			this.highlight(this.selectedList[i], true);
			// select the form field option
			this.form_select(this.selectedList[i], true);
		}
	};
	
	// run the constructor
	this.init();
}

function popUp(URL,width,height) {
	var left = (screen.width - width) / 2;
	var top = (screen.height - height) / 2;
	var size = "width=" + width + ", height=" + height +" ,left=" + left + " ,top="+ top;
	insert_window = window.open(URL, 'popup', 'toolbar=0,scrollbars=1,statusbar=0,menubar=0,resizable=1,'+size);
	insert_window.focus();
}

function pod_accordion_click(headerText,podID){
	if(typeof(pageTracker) != "undefined"){
		var labelText = headerText + " - [" + podID + "]";
		pageTracker._trackEvent("Pod-Accordion", "click", labelText);
	}
}

function say_hi(){
	alert("hi");
}

function set_cookie(name, value, domain, expires){
	var days;
	if(expires == undefined || expires == -1){ 
		days = expires == -1 ? -1 : 1; //default life of cookie, in days
	}else{
		days = expires; //life of cookie, in days
	}
	expires = new Date();
	expires.setTime( expires.getTime() + (days * 1000 * 60 * 60 * 24) );
	
	var str = name + "=" + value +"; path=/; expires=" + expires.toGMTString();
	
	str += ((domain != "" && domain != undefined)  ? "; domain=" + domain : "");

	document.cookie = str;
}

function set_cookie_filter(module, val) {
	set_cookie('cms_'+module+'_filter', val); 
}

function set_height(id, h){
	$(id).style.height = h + "px";
}

function set_left(id, h){
	$(id).style.left = h + "px";
}

function set_top(id, h){
	$(id).style.top = h + "px";
}

function set_width(id, w){
	$(id).style.width = w + "px";
}


function show_form_errors(form, jsonObj){
	_(jsonObj.fields).each(function(fld) {
		// reset the fields
		var temp = $("#field_" + fld.field);
		if(temp.length && temp.hasClass('warning')) {
			temp.removeClass('warning');

			// reset the error message
			if($("#message_" + fld.field).length){
				$("#message_" + fld.field).html("");
			}
		}
	});
	
	_(jsonObj.errors).each(function(err) {
		// change style to warning
		var temp = $("#field_" + err.field)
		temp.addClass('warning');
		// output the error message
		if($("#message_" + err.field)){
			$("#message_" + err.field).html(err.message);
		}
	});
}

function reveal_email(part1, part2, qstr){
	if(part1 != "" && part2 != ""){
		document.location = 'mailto:' + part2.reverse() + "@" + part1.reverse() + qstr;
	}
}

function showHide(id){
	var elem = $('#' + id);
	if (elem.css('display') == "none" || elem.css('display') == "") {
		showHideDivs(id, "show");
	} else {
		showHideDivs(id, "hide");
	}
}
function displayElem(id, hide){
	var elem = $('#' + id);
        if(hide){
	
		showHideDivs(id, "hide");
	}else{
		showHideDivs(id, "block");
	}
}
function hideShow(id){
	var elem = $('#' + id);
	if (elem.css('display') == "block" || elem.css('display') == "") {
		showHideDivs(id, "hide");
	}else{
		showHideDivs(id, "block");
	}
}

function showHideDivs() { //v6.0
	var i,p,v,obj,args=showHideDivs.arguments;
	for (i=0; i < (args.length-1); i+=2) {
		obj = args[i];
		if (typeof(obj) === 'string') {
			obj = $('#' + obj);
		}
		if (typeof(obj) !== 'undefined' && obj.length) { 
			v=args[i+1];
			if (obj.css) { 
				v= (v=='show') ? 'block' : ((v=='hide') ? 'none' : v);
				obj.css('display', v);
			}
		}
	}
}

function switch_to_https(){
	var url = window.location.href;
	if(url.indexOf("https") == -1 && url.indexOf("/admin/") < 0){
		p = url.indexOf("p");
		window.location = "https" + url.substr(p + 1);
	}
}

function submit_form(id){
	$('#' + id).submit();
}

function try_text_focus(form, field){
	if ($('#' + form).length) {
		var field = $("#" + form).get(0)[field];
		if (typeof(field) !== 'undefined') {
			field.focus();
		}
	}	
}
function downloadEvent(){
  //  alert('DOwnload Started');
    var url = MODULES_URL+"/contactus/controllers/fn-research-registration.php?formAction=download-event";
     window.open(url);
    //base_ajax('',url , 'formAction=download-event');
}
function waitDownload(url, delay){
    //alert('Hello');
     var t = setTimeout("downloadEvent()", 3000)
    
}

// **** TINY MCE FUNCTIONS FOR SIMPLE TEXT AREAS *** //

function load_tinymce(el){
	tinyMCE.init({
		mode : "exact",
		elements : el,
		theme : "advanced",
		skin : "thebigreason",
		convert_urls: false,
		plugins : "style,advhr,advimage,advlink,nonbreaking,paste,save",
		// Theme options
		theme_advanced_buttons1 : "formatselect,bold,italic,removeformat,|,bullist,numlist,|,justifyleft,justifycenter,justifyright,|,pastetext",
		theme_advanced_buttons2 : "",
		theme_advanced_buttons3 : "",
		theme_advanced_blockformats : "p,h4",
		valid_elements: "p,a[href|target=_blank],strong/b,div[align],br,h3,i,ul,li,ol",
		theme_advanced_toolbar_location : "top",
		theme_advanced_toolbar_align : "left",
		theme_advanced_statusbar_location : "bottom",
		theme_advanced_resizing : false
	});
}

function save_tinymce() {
	// broken-out function to tell active editor to save itself
	// could have been put in its primary caller "validate_form_ajax"
	// but the below function causes validate_form_ajax to stop????
	// plus, this way we can use it elsewhere.
	if(typeof tinyMCE !== 'undefined') {
		if(tinyMCE.activeEditor) {
			// does not equal null, so save()
			tinyMCE.activeEditor.save();
		}
	}
}

function upper_case_first(text){
	return text.substr(0, 1).toUpperCase() + text.substr(1);
}

function validate_require_all(form) {
	var readyVar = true;
	for (i=0; i<form.elements.length; i++) {
		if (form.elements[i].type != "hidden" && form.elements[i].type != "submit")
			if (form.elements[i].value=="") {
				form.elements[i].parentNode.className += " warning";
				readyVar = false;
			}
	}
	return readyVar;
}

String.prototype.reverse = function(){
	var newString = "";
	var counter = this.length;
	
	for(var i = counter; i > 0; i--){
	   newString += this.substring(i - 1, i); 
	}
	
	return newString;
}

// handle memory leaks
Function.prototype.closure = function(obj){
  // Init object storage.
  if (!window.__objs)
  {
    window.__objs = [];
    window.__funs = [];
  }

  // For symmetry and clarity.
  var fun = this;

  // Make sure the object has an id and is stored in the object store.
  var objId = obj.__objId;
  if (!objId)
    __objs[objId = obj.__objId = __objs.length] = obj;

  // Make sure the function has an id and is stored in the function store.
  var funId = fun.__funId;
  if (!funId)
    __funs[funId = fun.__funId = __funs.length] = fun;

  // Init closure storage.
  if (!obj.__closures)
    obj.__closures = [];

  // See if we previously created a closure for this object/function pair.
  var closure = obj.__closures[funId];
  if (closure)
    return closure;

  // Clear references to keep them out of the closure scope.
  obj = null;
  fun = null;

  // Create the closure, store in cache and return result.
  return __objs[objId].__closures[funId] = function ()
  {
    return __funs[funId].apply(__objs[objId], arguments);
  };
};
