/*
   Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
   of Simon Willison (see comments by Simon below).

   Description:
   	
   	Uses css selectors to apply javascript behaviours to enable
   	unobtrusive javascript in html documents.
   	
   Usage:   
   
	var myrules = {
		'b.someclass' : function(element){
			element.onclick = function(){
				alert(this.innerHTML);
			}
		},
		'#someid u' : function(element){
			element.onmouseover = function(){
				this.innerHTML = "BLAH!";
			}
		}
	};
	
	Behaviour.register(myrules);
	
	// Call Behaviour.apply() to re-apply the rules (if you
	// update the dom, etc).

   License:
   
   	This file is entirely BSD licensed.
   	
   More information:
   	
   	http://ripcord.co.nz/behaviour/
   
*/   

var Behaviour = {
	list : new Array,
	
	register : function(sheet){
		Behaviour.list.push(sheet);
	},
	
	start : function(){
		Behaviour.addLoadEvent(function(){
			Behaviour.apply();
		});
	},
	
	apply : function(){
		for (h=0;sheet=Behaviour.list[h];h++){
			for (selector in sheet){
				list = document.getElementsBySelector(selector);
				
				if (!list){
					continue;
				}

				for (i=0;element=list[i];i++){
					sheet[selector](element);
				}
			}
		}
	},
	
	addLoadEvent : function(func){
		var oldonload = window.onload;
		
		if (typeof window.onload != 'function') {
			window.onload = func;
		} else {
			window.onload = function() {
				oldonload();
				func();
			}
		}
	}
}

Behaviour.start();

/*
   The following code is Copyright (C) Simon Willison 2004.

   document.getElementsBySelector(selector)
   - returns an array of element objects from the current document
     matching the CSS selector. Selectors can contain element names, 
     class names and ids and can be nested. For example:
     
       elements = document.getElementsBySelect('div#main p a.external')
     
     Will return an array of all 'a' elements with 'external' in their 
     class attribute that are contained inside 'p' elements that are 
     contained inside the 'div' element which has id="main"

   New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
   See http://www.w3.org/TR/css3-selectors/#attribute-selectors

   Version 0.4 - Simon Willison, March 25th 2003
   -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
   -- Opera 7 fails 
*/

function getAllChildren(e) {
  // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  return e.all ? e.all : e.getElementsByTagName('*');
}

document.getElementsBySelector = function(selector) {
  // Attempt to fail gracefully in lesser browsers
  if (!document.getElementsByTagName) {
    return new Array();
  }
  // Split selector in to tokens
  var tokens = selector.split(' ');
  var currentContext = new Array(document);
  for (var i = 0; i < tokens.length; i++) {
    token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
    if (token.indexOf('#') > -1) {
      // Token is an ID selector
      var bits = token.split('#');
      var tagName = bits[0];
      var id = bits[1];
      var element = document.getElementById(id);
      if (tagName && element.nodeName.toLowerCase() != tagName) {
        // tag with that ID not found, return false
        return new Array();
      }
      // Set currentContext to contain just this element
      currentContext = new Array(element);
      continue; // Skip to next token
    }
    if (token.indexOf('.') > -1) {
      // Token contains a class selector
      var bits = token.split('.');
      var tagName = bits[0];
      var className = bits[1];
      if (!tagName) {
        tagName = '*';
      }
      // Get elements matching tag, filter them for class selector
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      continue; // Skip to next token
    }
    // Code to deal with attribute selectors
    if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
      var tagName = RegExp.$1;
      var attrName = RegExp.$2;
      var attrOperator = RegExp.$3;
      var attrValue = RegExp.$4;
      if (!tagName) {
        tagName = '*';
      }
      // Grab all of the tagName elements within current context
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      var checkFunction; // This function will be used to filter the elements
      switch (attrOperator) {
        case '=': // Equality
          checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
          break;
        case '~': // Match one of space seperated words 
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
          break;
        case '|': // Match start with value followed by optional hyphen
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
          break;
        case '^': // Match starts with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
          break;
        case '$': // Match ends with value - fails with "Warning" in Opera 7
          checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
          break;
        case '*': // Match ends with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
          break;
        default :
          // Just test for existence of attribute
          checkFunction = function(e) { return e.getAttribute(attrName); };
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (checkFunction(found[k])) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
      continue; // Skip to next token
    }
    
    if (!currentContext[0]){
    	return;
    }
    
    // If we get here, token is JUST an element (not a class or ID selector)
    tagName = token;
    var found = new Array;
    var foundCount = 0;
    for (var h = 0; h < currentContext.length; h++) {
      var elements = currentContext[h].getElementsByTagName(tagName);
      for (var j = 0; j < elements.length; j++) {
        found[foundCount++] = elements[j];
      }
    }
    currentContext = found;
  }
  return currentContext;
}

/* That revolting regular expression explained 
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
  \---/  \---/\-------------/    \-------/
    |      |         |               |
    |      |         |           The value
    |      |    ~,|,^,$,* or =
    |   Attribute 
   Tag
*/

var tabBody = 'table-row';
if (document.all) {
  tabBody = 'block';
}
function showTab(rowname, tabClass) { 
  var tabs = document.getElementsBySelector('li.'+tabClass);
  if (tabs) {
	  for(var i = 0; i < tabs.length; i++) {
	    var tab = tabs[i];
	    var row = document.getElementById(tab.id + 'Contents');
	
	    row.style.display = 'none';
	    tab.className = tabClass;
	  }
  }
  
  var row = document.getElementById(rowname + 'Contents');
  var tab = document.getElementById(rowname);
  row.style.display = tabBody;
  tab.className = tabClass + ' selected' + tabClass;
  if (tab.getAttribute("url") != null) {
  	loadPage(tab.getAttribute("url"), rowname+'InnerContents'); 
  } else {
    repositionFooter();
  }
}

function showDynamicTab(rowname) { 
  showTab(rowname, "dynamictab");
  var tab = document.getElementById(rowname);
  var url = tab.getAttribute("baseUrl")+"/"+rowname;
  if (tab.pageid) url = url + "/" + tab.pageid;
  loadPage(url, rowname+"Contents");
}

function showAccordion(rowname, tabClass) { 
  var tabs = document.getElementsBySelector('h2.'+tabClass);
  if (tabs) {
	  for(var i = 0; i < tabs.length; i++) {
	    var tab = tabs[i];
	    var row = document.getElementById(tab.id + 'Contents');
	
	    row.style.display = 'none';
	    tab.className = tabClass;
	  }
  }
  
  var row = document.getElementById(rowname + 'Contents');
  var tab = document.getElementById(rowname);
  row.style.display = tabBody;
  tab.className = tabClass + ' selected' + tabClass;
}

function requiredInputField(el) {
    doRequiredValue(el.value, el.name + 'RequiredImage');
    el.onkeyup = function() {
      doRequiredValue(el.value, el.name + 'RequiredImage');
    }
}

function mouseOverImage(el) {
    el.onmouseover = function() {
      if(el.src.indexOf('_over') > 0) return;
      el.src = el.src.substring(0, el.src.length - 4) + '_over' + '.' + el.src.substring(el.src.length - 3);
    },
    el.onmouseout = function() {
      if(el.src.indexOf('_over') < 0) return;
      el.src = el.src.substring(0, el.src.length - 9) + '.' + el.src.substring(el.src.length - 3);
    }
}
  
function doRequiredValue(value, imageId) {
  var image = document.getElementById(imageId);
  if (value == null || value == '') {
    image.src = '/i/question.gif';
  } else {
    image.src = '/i/checked.gif';
  }
}

function underlineBox(el) {
    var oldFocus = el.onfocus;
    el.onfocus = function() {
      el.className = 'underlineHover';
      if (oldFocus) oldFocus();
    };
    var oldBlur = el.onblur;
    el.onblur = function() {
      el.className = 'underline';
      if (oldBlur) oldBlur();
    }
}

var tabRules = {
  'li.tab' : function(el) {
    el.onclick = function() {
      showTab(el.id, el.className);
    }
  }
};

var dynamicTabRules = {
  'li.dynamictab' : function(el) {
    el.onclick = function() {
      showDynamicTab(el.id);
    }
  }
};
  
var accordionRules = {  
  'h2.accordion' : function(el) {
    el.onclick = function() {
      showAccordion(el.id, el.className);
    }
  }
};

var requiredRules = {
  'input.required' : requiredInputField,
  'textarea.required' : requiredInputField
};

var mouseOverRules = {
  'input.mouseOver' : mouseOverImage,
  'img.mouseOver' : mouseOverImage
};

var underlineBoxRules = {
  'input.underline' : underlineBox
};

Behaviour.register(tabRules);
Behaviour.register(dynamicTabRules);
Behaviour.register(accordionRules);
Behaviour.register(mouseOverRules);
Behaviour.register(requiredRules);
Behaviour.register(underlineBoxRules);
    
function TJ_HTTP() {
  if (window.XMLHttpRequest) {
    this.xmlHttp = new XMLHttpRequest();
  } else {
    this.xmlHttp = new ActiveXObject('Msxml2.XMLHTTP')||new ActiveXObject('Microsoft.XMLHTTP');
  }
}

TJ_HTTP.prototype.asyncStateChange = function() {
  if (this.xmlHttp.readyState==4) {
    var status = 0;
    var statusText = "";
    var response = "";
    try {status = this.xmlHttp.status; response=this.xmlHttp.responseText; statusText=this.xmlHttp.statusText} catch(e){}
    if (status==200 || status == 0)
      this.callback(response);
    else
      this.callback("Error:"+statusText);
    delete this.xmlHttp;
  }
}

TJ_HTTP.prototype.getAsync = function(url, callback) {
  this.callback = callback;
  this.xmlHttp.open("GET", url, true); 
  var tjHttp = this;
  this.xmlHttp.onreadystatechange=function(){tjHttp.asyncStateChange()};
  this.xmlHttp.send(null); 
}

TJ_HTTP.prototype.postAsync = function(url, params, callback) {
  this.callback = callback;
  this.xmlHttp.open("POST", url, true); 
  this.xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  this.xmlHttp.setRequestHeader("Content-length", params.length);
  var tjHttp = this;
  this.xmlHttp.onreadystatechange=function(){tjHttp.asyncStateChange()};
  this.xmlHttp.send(params);
}
var UP=38;var DOWN=40;var ESC=27;var ENTER=13; var TAB=9;
var searchInProgress = false;
var __ajaxTimer;
var __timerId;
var __keyPressed=0;
var __timeout = 100;
var keyArr;
var _numSuggestions = 0;
var arrowIndex=-1;
var suggest_cache = {};
var startTime=null;
var responseTime=null;

function xajax_getCities(elem, val){
  var tjHttp = new TJ_HTTP();
  var airportOnly = $(elem).getAttribute('airportCitiesOnly')?1:0;
  $(elem).addClassName("ajax_loading");
  tjHttp.getAsync('/i/citysuggest.php?e='+elem+'&v='+escape(val)+'&a='+airportOnly, function(responseText)
    {
	  $(elem).removeClassName('ajax_loading');
	  urchinTracker("/ajax/citysuggest");
      eval("window.response = "+responseText);
      var response = window.response;
      suggestValues(response.elementId, response.values, response.keys, response.searchValue, response.nearestAirports);
    });
}
function autoSuggestFocus(elementId) {
    var element = $(elementId);
    if (element.getAttribute('programFocus') == 'yes') {
       element.setAttribute('programFocus', '');
       return;
    }
    if (element.value == element.getAttribute("info")) {
        element.value = "";
        if (element.getAttribute('mtd') != 'City')
            element.onkeyup();
    } else {
        element.select();
        if (element.getAttribute("autoSuggestOnFocus") == element.value && element.value != element.getAttribute("oldValue")) {
            if (element.onkeyup)
                element.onkeyup();
        } else if (element.onkeyup && element.value != "" && element.value != element.getAttribute("oldValue")) {
            var newValue = element.value;
            newValue = newValue.substring(0,3);
        	element.setAttribute('goBackMode', 'yes');
            if (element.getAttribute('cache') == 1) {
              var key = element.getAttribute('mtd')+"_"+newValue.replace(/[^a-z0-9]/gi,'_');
              if (typeof(suggest_cache[key]) != 'undefined') {
                suggestValues(elementId, suggest_cache[key].values, suggest_cache[key].keys, newValue, suggest_cache[key].nearestAirports);
                return;
              }
            }
            var jsNewValue = newValue.replace(/\\/g,"\\\\");
            eval("xajax_"+element.getAttribute("mtd")+"('"+elementId+"','"+jsNewValue+"')");
        }
    }
    element.className = element.className.replace("infoText", "");
}

function searchSuggest(elementId, method, timeout, event) {
	var keyPressed = 0;
	if (window.event) keyPressed = window.event.keyCode;  else keyPressed = !event?0:event.which;
    var element = $(elementId);
    var newValue = $(elementId).value;
    if (!timeout) newTimeout=__timeout; else newTimeout = timeout;
    if (keyPressed == ENTER) {
      if (element.getAttribute('onenter')) {
	    element.blur();
        eval(element.getAttribute('onenter'));
        return;
      }
    }
	if (element.getAttribute("oldValue") == newValue && newValue != "") { 
	   return;
	}
    __keyPressed = new Date().getTime();
    
	if (searchInProgress) {
		waitForSearchCompletion(elementId, method, timeout);
		return;
	}
    
    clearTimeout(__ajaxTimer);
	__ajaxTimer = setTimeout(
        function() {
        	$(elementId).setAttribute("oldValue", newValue);
        	$(elementId+"_id").value = "";
            if (element.getAttribute('cache') == 1) {
              var key = element.getAttribute('mtd')+"_"+newValue.replace(/[^a-z0-9]/gi,'_');
              if (typeof(suggest_cache[key]) != 'undefined') {
                suggestValues(elementId, suggest_cache[key].values, suggest_cache[key].keys, newValue, suggest_cache[key].nearestAirports);
          	    __ajaxTimer = null;
                return;
              }
            }
  	        searchInProgress=true;
        	var jsNewValue = newValue.replace(/\\/g,"\\\\");
        	jsNewValue = jsNewValue.replace(/'/g,"\\'");
        	eval(method+"('"+elementId+"','"+jsNewValue+"')");
        	__ajaxTimer = null;
        }	
     	, timeout);
}

function waitForSearchCompletion(elementId, method, timeout, waitCount) {
    if (!waitCount) waitCount = 0;
	clearTimeout(__timerId);
	if (!searchInProgress || waitCount > 50) {
	  searchInProgress = false;
	  searchSuggest(elementId, method, timeout);
	  __timerId = null;
	} else {
	  waitCount++;
   	  __timerId = setTimeout("waitForSearchCompletion('"+elementId+"','"+method+"',"+timeout+","+waitCount+")", timeout);
   	}
}

function getCitySuggestRow(idx, elementId, key, value, airportImage) {
    var actualText = value.replace(/<\/?SPAN>/gi,"");
    actualText = actualText.replace(/[^a-zA-Z]/gi,"");
    var suggest = '<div onmouseover="javascript:suggestOver(this,'+idx+', 1);" id="'+elementId+'_options_'+idx+'" ';
    suggest += 'onmouseout="javascript:suggestOut(this,'+idx+');" cityId="'+key+'" cityVal="'+value+'"';
    suggest += 'onclick="javascript:setSearch(\''+elementId+'\', '+idx+');" ';
    suggest += 'class="suggest_link" secondId="'+elementId+'_'+actualText+'">' ;
    if (airportImage)
    	suggest += '<img src="/i/images/airport.gif" class="vm" border="0" style="padding-left:3px;padding-right:3px;"/>'
    suggest += value;
    suggest += '</div>\n';
    return suggest;
}

function suggestValues(elementId, values, keys, searchValue, nearestAirports) {
    hideOtherSuggest(elementId);
	arrowIndex=-1;
	if (typeof(values) != 'object') {
    	var valArr = values.split("|");
   	    keyArr = keys.split("|");
    } else {
        var valArr = values;
        keyArr = keys;
    }
	var element = $(elementId);
	if (element.getAttribute('cache') == 1) {
	  suggest_cache[element.getAttribute('mtd')+"_"+searchValue.replace(/[^a-z0-9]/gi,'_')] = {'values':values, 'keys':keys, 'nearestAirports': nearestAirports};
	}
	var idiv = $(elementId+"_suggest");
	var sDivContent = "";
	idiv.innerHTML="";
	var defaultSet = false;
	if (element.getAttribute('mtd') == 'CityXX' && element.getAttribute('goBackMode') != "yes" && !__timerId) {
    	var lowerCityName = element.value.toLowerCase().trim();
    	if (lowerCityName != "") {
        	if (lowerCityName.indexOf(",") < 0) lowerCityName = lowerCityName+",";
        	for (var i=0;i < valArr.length;i++) {
        	  var valWithoutHighlight = valArr[i].replace(/<\/?SPAN>/gi,"").toLowerCase();
        	  if (valWithoutHighlight.startsWith(lowerCityName) || valWithoutHighlight.indexOf("("+lowerCityName.replace(",","")+")") >= 0 || valArr.length == 2) {
            	  setAutoSuggestValue(elementId, keyArr[0], valArr[0].replace(/<\/?SPAN>/gi,""));
                  hideSuggest(elementId);
              	  defaultSet = true;
              	  searchInProgress = false;
              	  return;
        	  }
        	}
    	}
	}
	element.setAttribute('goBackMode', '');
	var k=0;
	for(var i=0; i < valArr.length; i++) {
	    if (valArr[i] == '') continue;
        var airportSupported = (nearestAirports && nearestAirports[keyArr[i]] && nearestAirports[keyArr[i]][0] && !element.getAttribute('noAirports') && !valArr[i].match(/.+\(.+\)/g));
        var j=0;
        var suggest = '';
        if (airportSupported && nearestAirports[keyArr[i]][0][1].match(/.+\-\sAll Airports.+/i) && nearestAirports[keyArr[i]][0][2] == keyArr[i]) {
        	suggest += getCitySuggestRow(k, elementId, keyArr[i], nearestAirports[keyArr[i]][0][1], false);
        	j++;
        } else {
        	suggest += getCitySuggestRow(k, elementId, keyArr[i], valArr[i], false);
        }
        k++;
        if (airportSupported) {
        	for (;j<nearestAirports[keyArr[i]].length;j++) {
            	suggest += getCitySuggestRow(k, elementId, keyArr[i], nearestAirports[keyArr[i]][j][1], true);
                k++;
        	}
        }
        sDivContent += suggest;
	}
	_numSuggestions = k;
	if (element.getAttribute("noAdd") != "1") {
	    var method = element.getAttribute("mtd");
		sDivContent += eval("get"+method+"AddLink('"+elementId+"')");
	}
	if (sDivContent != "") { 
    	//sDivContent = '<div class="closeSuggest"><a href="#" onclick="hideOtherSuggest(null);return false;"><img src="/i/images/close.gif"/></a></div>'+sDivContent;		
    	if (IsIE()) { 
    		sInnerHtml = "<div id='" + elementId + "_suggest_content'>";
    		sInnerHtml += sDivContent;
    		sInnerHtml += "</div>";
    		sInnerHtml += "<iframe id='" + elementId + "_suggest_iframe' src='/i/blank.html' frameborder='1' scrolling='no'></iframe>";
    		idiv.innerHTML = sInnerHtml;
    		var divContent=$(elementId + "_suggest_content");
    		var divIframe=$(elementId + "_suggest_iframe");
    				
    		divContent.className="suggestOverBase";
    		idiv.className="suggestBase";
    
    		if (divContent.offsetHeight >= 200) {
    			divContent.style.height = "200px";
    		} else {
    			divContent.style.height = divContent.offsetHeight+'px';
    		}
    		divContent.style.width = (divContent.offsetWidth+20)+"px";
    		divContent.style.overflow = "auto";
    		
    		divIframe.style.width = (divContent.offsetWidth) + 'px';
    		divIframe.style.height = divContent.offsetHeight + 'px';
    		divIframe.marginTop = "-" + divContent.offsetHeight + 'px';
    	}
    	else {
    		sInnerHtml=sDivContent;
    		idiv.innerHTML = sInnerHtml;
    		idiv.className="suggest_box visible";
    	}
    	element.onkeydown = keyDown;
    } else {
      hideSuggest(elementId);
    }
	searchInProgress=false;
}

function selectRange(element, iStart, iLength) {
    //use text ranges for Internet Explorer
    if (element.createTextRange) {
        var oRange = element.createTextRange(); 
        oRange.moveStart("character", iStart); 
        oRange.moveEnd("character", iLength - element.value.length);      
        oRange.select();
        
    //use setSelectionRange() for Mozilla
    } else if (element.setSelectionRange) {
        element.setSelectionRange(iStart, iLength);
    }     

    //set focus back to the textbox
    element.focus();      
}


function getCityAddLink(elementId){
    var element = $(elementId);
    if (element.value.trim != "")
        return '<div id="'+elementId+'_add" class="suggest_link"><a href="#" onfocus="this.blur();" onclick="addAutoSuggestItem(\''+elementId+'\');return false;">Can\'t find a city? Add it -> <span>'+element.value+'</span></a></div>';
}

function getTagAddLink(elementId){
    var element = $(elementId);
    if (element.value.trim() != "")
        return '<div class="suggest_link"><a href="#" onfocus="this.blur();" onclick="addAutoSuggestItem(\''+elementId+'\');return false;">can\'t find the right label? Add it -> <span>'+element.value+'</span></a></div>';
    else
        return '<div class="suggest_link"><a href="#" onfocus="this.blur();" onclick="addAutoSuggestItem(\''+elementId+'\');return false;">Can\'t find the right label? Add new >> </a></div>';
}

function getVenueAddLink(elementId){
    var element = $(elementId);
    if (element.value.trim() != "")
        return '<div class="suggest_link"><a href="#" onfocus="this.blur();" onclick="addAutoSuggestItem(\''+elementId+'\');return false;">can\'t find the right Venue? Add it -> <span>'+element.value+'</span></a></div>';
    else
        return '<div class="suggest_link"><a href="#" onfocus="this.blur();" onclick="addAutoSuggestItem(\''+elementId+'\');return false;">Can\'t find the right Venue? Add new >> </a></div>';
}

function addAutoSuggestItem(elementId) {
    eval("suggestAdd"+$(elementId).getAttribute("mtd")+"('"+elementId+"')");
}

function suggestAddCity(elementId) {
	var element = $(elementId);
	hideSuggest(elementId);
	openPlainWindow('/city/add?cityname='+element.value+'&elementId='+elementId, 500, 400);
}

function suggestAddTag(elementId) {
	var element = $(elementId);
	hideSuggest(elementId);
	var value = element.value;
	if (value == element.getAttribute("info"))
	   value = "";
	openPlainWindow('/tag/add?tagname='+value+'&elementId='+elementId+'&tagType='+element.getAttribute('tagType'), 500, 400);
}

function suggestAddVenue(elementId) {
	var element = $(elementId);
	hideSuggest(elementId);
	var value = element.value;
	if (value == element.getAttribute("info"))
	   value = "";
	openPlainWindow('/venue/add?name='+value+'&elementId='+elementId, 500, 400);
}

function setAutoSuggestValue(elementId, id, value) {
	if ($(elementId)) {
		$(elementId).value = value;
		$(elementId).setAttribute("oldValue", value);
		$(elementId+"_id").value = id;
		if ($(elementId).getAttribute('copyTo')) {
			setAutoSuggestValue($(elementId).getAttribute('copyTo'), id, value);
		}
	}
}

window.setAutoSuggestValue = setAutoSuggestValue;

function keyDown(e) { 
	var keyPressed = 0;
	if (document.all) keyPressed = window.event.keyCode;  else keyPressed = e.which;
	switch(keyPressed){
		case UP: arrowSelection(this,'UP');break;
		case DOWN: arrowSelection(this,'DOWN');break;
		case TAB: 
		case ENTER: 
		        if (this.value != this.getAttribute("oldValue")) {
		           return true;
		        }
		        var element = this;
		        var fn = function() {
                	if (searchInProgress || __ajaxTimer) {
                   	   setTimeout(fn, 100);
                   	   return;
                   	}                    
                   	if (arrowIndex == -1) arrowIndex = 0;
    				if(arrowIndex >= 0 && arrowIndex < _numSuggestions ){ 
    					var tempObj=document.getElementById(element.id+'_options_'+arrowIndex);
    					if (!tempObj) 
        					hideSuggest(element.id);
    		            else {
    		                setSearch(element.id, arrowIndex);
    		                if (keyPressed == ENTER) {
    		                  if (element.getAttribute('onenter')) {
                        	    element.blur();
    		                    eval(element.getAttribute('onenter'));
    		                  }
    		                }
    		            }
    		        } 
		        }
		        fn();
				return false; 
		case ESC:hideSuggest(this.id);break;
		default: break;
	}
}

function lostFocus(id, loopCount) {
    if (!loopCount) loopCount=1;
	if ((searchInProgress || __ajaxTimer) && loopCount < 50) {
	   loopCount++;
   	   setTimeout("lostFocus('"+id+"',"+loopCount+");", 100);
   	   return;
   	}
    if (arrowIndex == -1) arrowIndex = 0;
	if(arrowIndex >= 0 && keyArr && arrowIndex < _numSuggestions ){ 
	  var tempObj=document.getElementById(id+'_options_'+arrowIndex);
	  if (tempObj) 
        setSearch(id, arrowIndex);
    }
	setTimeout('hideSuggest("'+id+'");', 500);
}

function arrowSelection(obj,index) { 
    if (!keyArr) return;
	var suggestboxoldid;
	var arrLen = _numSuggestions-1;
	var newArrowIndex = arrowIndex;
	if(index=='DOWN') {
		newArrowIndex++;
	} else {
		newArrowIndex--;
	}
    if(newArrowIndex < 0) return; 
    if (newArrowIndex > arrLen) {
      if ($(obj.id+"_add")) $(obj.id+"_add").scrollIntoView(false);
	  return;
	} 
	suggestbox=$(obj.id+'_options_'+newArrowIndex);	
	suggestOver(suggestbox,newArrowIndex);
}

function suggestOver(div_value,indexVal, bymouse) { 
    if (!keyArr) return;
    var tmpIndex=arrowIndex<0?0:arrowIndex;
    if (div_value) {
        var suggestboxold=$(div_value.id.replace("_"+indexVal, "_"+tmpIndex));
    	suggestOut(suggestboxold);
    	div_value.className = 'suggest_link_over';
	}
	arrowIndex=indexVal;
	if (!bymouse && div_value)
       	div_value.scrollIntoView(false);
}

function suggestOut(div_value) {
	arrowIndex=-1;
	if (div_value)
    	div_value.className = 'suggest_link';
}

function setSearch(elementId, suggestKey) {
	var displayValue = $(elementId+"_options_"+suggestKey).getAttribute('cityVal');
	displayValue = displayValue.replace(/<\/?SPAN>/gi,"");
	displayValue = displayValue.replace(/<img[^\>].+\>/gi,"");
	var displayKey = $(elementId+"_options_"+suggestKey).getAttribute('cityId');
	setAutoSuggestValue(elementId, displayKey, displayValue);
	hideSuggest(elementId);
}

function hideSuggest(elementId) {
    var element = $(elementId);
	$(elementId+"_suggest").className = 'suggest_box';
	element.onkeydown = null;
	if (element.value.trim() == "" && element.getAttribute("info") != null) {
	   element.value = element.getAttribute("info");
       element.className = element.className + " infoText";
    } 
    keyArr = null;
    arrowIndex = -1;
    _numSuggestions = 0;
}

function hideOtherSuggest(excludeElementId) {
    try {
        var textBoxes = document.getElementsByTagName("input");
        for(var i=0;i<textBoxes.length;i++) {
           if (textBoxes[i].type == "text" && textBoxes[i].id != excludeElementId && $(textBoxes[i].id+"_suggest")) {
              hideSuggest(textBoxes[i].id);
           }
        }
    } catch(e) {}
}

function showSuggest(elementId) {
    if($(elementId+"_suggest").innerHTML != "") {
    	$(elementId+"_suggest").className = 'suggestBase';
    	$(elementId).onkeydown = keyDown;
	}
}

function IsIE() {
	return ( navigator.appName=="Microsoft Internet Explorer" ); 
}
	
function IsNav() {
	return ( navigator.appName=="Netscape" );
}

/*  Prototype JavaScript framework, version 1.5.0
 *  (c) 2005-2007 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.5.0',
  BrowserFeatures: {
    XPath: !!document.evaluate
  },

  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
  emptyFunction: function() {},
  K: function(x) { return x }
}

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

var Abstract = new Object();

Object.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (object === undefined) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : object.toString();
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({}, object);
  }
});

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this, args = $A(arguments), object = args.shift();
  return function(event) {
    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  }
}

Object.extend(Number.prototype, {
  toColorPart: function() {
    var digits = this.toString(16);
    if (this < 16) return '0' + digits;
    return digits;
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  }
});

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) {}
    }

    return returnValue;
  }
}

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.callback(this);
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
}
String.interpret = function(value){
  return value == null ? '' : String(value);
}

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = count === undefined ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return this;
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : this;
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  escapeHTML: function() {
    var div = document.createElement('div');
    var text = document.createTextNode(this);
    div.appendChild(text);
    return div.innerHTML;
  },

  unescapeHTML: function() {
    var div = document.createElement('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return {};

    return match[1].split(separator || '&').inject({}, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var name = decodeURIComponent(pair[0]);
        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;

        if (hash[name] !== undefined) {
          if (hash[name].constructor != Array)
            hash[name] = [hash[name]];
          if (value) hash[name].push(value);
        }
        else hash[name] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function(){
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.replace(/\\/g, '\\\\');
    if (useDoubleQuotes)
      return '"' + escapedString.replace(/"/g, '\\"') + '"';
    else
      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (typeof replacement == 'function') return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
}

String.prototype.parseQuery = String.prototype.toQueryParams;

var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern  = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    return this.template.gsub(this.pattern, function(match) {
      var before = match[1];
      if (before == '\\') return match[2];
      return before + String.interpret(object[match[3]]);
    });
  }
}

var $break    = new Object();
var $continue = new Object();

var Enumerable = {
  each: function(iterator) {
    var index = 0;
    try {
      this._each(function(value) {
        try {
          iterator(value, index++);
        } catch (e) {
          if (e != $continue) throw e;
        }
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator) {
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.map(iterator);
  },

  all: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      result = result && !!(iterator || Prototype.K)(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator) {
    var result = false;
    this.each(function(value, index) {
      if (result = !!(iterator || Prototype.K)(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      results.push((iterator || Prototype.K)(value, index));
    });
    return results;
  },

  detect: function(iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(pattern, iterator) {
    var results = [];
    this.each(function(value, index) {
      var stringValue = value.toString();
      if (stringValue.match(pattern))
        results.push((iterator || Prototype.K)(value, index));
    })
    return results;
  },

  include: function(object) {
    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = fillWith === undefined ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator) {
    var trues = [], falses = [];
    this.each(function(value, index) {
      ((iterator || Prototype.K)(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator) {
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (typeof args.last() == 'function')
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
}

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0, length = iterable.length; i < length; i++)
      results.push(iterable[i]);
    return results;
  }
}

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse)
  Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value && value.constructor == Array ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  indexOf: function(object) {
    for (var i = 0, length = this.length; i < length; i++)
      if (this[i] == object) return i;
    return -1;
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function() {
    return this.inject([], function(array, value) {
      return array.include(value) ? array : array.concat([value]);
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});

Array.prototype.toArray = Array.prototype.clone;

function $w(string){
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if(window.opera){
  Array.prototype.concat = function(){
    var array = [];
    for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for(var i = 0, length = arguments.length; i < length; i++) {
      if(arguments[i].constructor == Array) {
        for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  }
}
var Hash = function(obj) {
  Object.extend(this, obj || {});
};

Object.extend(Hash, {
  toQueryString: function(obj) {
    var parts = [];

	  this.prototype._each.call(obj, function(pair) {
      if (!pair.key) return;

      if (pair.value && pair.value.constructor == Array) {
        var values = pair.value.compact();
        if (values.length < 2) pair.value = values.reduce();
        else {
        	key = encodeURIComponent(pair.key);
          values.each(function(value) {
            value = value != undefined ? encodeURIComponent(value) : '';
            parts.push(key + '=' + encodeURIComponent(value));
          });
          return;
        }
      }
      if (pair.value == undefined) pair[1] = '';
      parts.push(pair.map(encodeURIComponent).join('='));
	  });

    return parts.join('&');
  }
});

Object.extend(Hash.prototype, Enumerable);
Object.extend(Hash.prototype, {
  _each: function(iterator) {
    for (var key in this) {
      var value = this[key];
      if (value && value == Hash.prototype[key]) continue;

      var pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  },

  keys: function() {
    return this.pluck('key');
  },

  values: function() {
    return this.pluck('value');
  },

  merge: function(hash) {
    return $H(hash).inject(this, function(mergedHash, pair) {
      mergedHash[pair.key] = pair.value;
      return mergedHash;
    });
  },

  remove: function() {
    var result;
    for(var i = 0, length = arguments.length; i < length; i++) {
      var value = this[arguments[i]];
      if (value !== undefined){
        if (result === undefined) result = value;
        else {
          if (result.constructor != Array) result = [result];
          result.push(value)
        }
      }
      delete this[arguments[i]];
    }
    return result;
  },

  toQueryString: function() {
    return Hash.toQueryString(this);
  },

  inspect: function() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }
});

function $H(object) {
  if (object && object.constructor == Hash) return object;
  return new Hash(object);
};
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
}

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (typeof responder[callback] == 'function') {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) {}
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate: function() {
    Ajax.activeRequestCount++;
  },
  onComplete: function() {
    Ajax.activeRequestCount--;
  }
});

Ajax.Base = function() {};
Ajax.Base.prototype = {
  setOptions: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   ''
    }
    Object.extend(this.options, options || {});

    this.options.method = this.options.method.toLowerCase();
    if (typeof this.options.parameters == 'string')
      this.options.parameters = this.options.parameters.toQueryParams();
  }
}

Ajax.Request = Class.create();
Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  _complete: false,

  initialize: function(url, options) {
    this.transport = Ajax.getTransport();
    this.setOptions(options);
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = this.options.parameters;

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    params = Hash.toQueryString(params);
    if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='

    // when GET, append parameters to URL
    if (this.method == 'get' && params)
      this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;

    try {
      Ajax.Responders.dispatch('onCreate', this, this.transport);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous)
        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      var body = this.method == 'post' ? (this.options.postBody || params) : null;

      this.transport.send(body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (typeof extras.push == 'function')
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    return !this.transport.status
        || (this.transport.status >= 200 && this.transport.status < 300);
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState];
    var transport = this.transport, json = this.evalJSON();

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + this.transport.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(transport, json);
      } catch (e) {
        this.dispatchException(e);
      }

      if ((this.getHeader('Content-type') || 'text/javascript').strip().
        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
          this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
      Ajax.Responders.dispatch('on' + state, this, transport, json);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name);
    } catch (e) { return null }
  },

  evalJSON: function() {
    try {
      var json = this.getHeader('X-JSON');
      return json ? eval('(' + json + ')') : null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval(this.transport.responseText);
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  initialize: function(container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    }

    this.transport = Ajax.getTransport();
    this.setOptions(options);

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function(transport, param) {
      this.updateContent();
      onComplete(transport, param);
    }).bind(this);

    this.request(url);
  },

  updateContent: function() {
    var receiver = this.container[this.success() ? 'success' : 'failure'];
    var response = this.transport.responseText;

    if (!this.options.evalScripts) response = response.stripScripts();

    if (receiver = $(receiver)) {
      if (this.options.insertion)
        new this.options.insertion(receiver, response);
      else 
        receiver.update(response);
    }

    if (this.success()) {
      if (this.onComplete)
        setTimeout(this.onComplete.bind(this), 10);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(container, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = {};
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(request) {
    if (this.options.decay) {
      this.decay = (request.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = request.responseText;
    }
    this.timer = setTimeout(this.onTimerEvent.bind(this),
      this.decay * this.frequency * 1000);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (typeof element == 'string')
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(query.snapshotItem(i));
    return results;
  };
}

document.getElementsByClassName = function(className, parentElement) {
  if (Prototype.BrowserFeatures.XPath) {
    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
    return document._getElementsByXPath(q, parentElement);
  } else {
    var children = ($(parentElement) || document.body).getElementsByTagName('*');
    var elements = [], child;
    for (var i = 0, length = children.length; i < length; i++) {
      child = children[i];
      if (Element.hasClassName(child, className))
        elements.push(Element.extend(child));
    }
    return elements;
  }
};

/*--------------------------------------------------------------------------*/

if (!window.Element)
  var Element = new Object();

Element.extend = function(element) {
  if (!element || _nativeExtensions || element.nodeType == 3) return element;

  if (!element._extended && element.tagName && element != window) {
    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;

    if (element.tagName == 'FORM')
      Object.extend(methods, Form.Methods);
    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
      Object.extend(methods, Form.Element.Methods);

    Object.extend(methods, Element.Methods.Simulated);

    for (var property in methods) {
      var value = methods[property];
      if (typeof value == 'function' && !(property in element))
        element[property] = cache.findOrStore(value);
    }
  }

  element._extended = true;
  return element;
};

Element.extend.cache = {
  findOrStore: function(value) {
    return this[value] = this[value] || function() {
      return value.apply(null, [this].concat($A(arguments)));
    }
  }
};

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    $(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    $(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, html) {
    html = typeof html == 'undefined' ? '' : html.toString();
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  replace: function(element, html) {
    element = $(element);
    html = typeof html == 'undefined' ? '' : html.toString();
    if (element.outerHTML) {
      element.outerHTML = html.stripScripts();
    } else {
      var range = element.ownerDocument.createRange();
      range.selectNodeContents(element);
      element.parentNode.replaceChild(
        range.createContextualFragment(html.stripScripts()), element);
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $A($(element).getElementsByTagName('*'));
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (typeof selector == 'string')
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    return Selector.findElement($(element).ancestors(), expression, index);
  },

  down: function(element, expression, index) {
    return Selector.findElement($(element).descendants(), expression, index);
  },

  previous: function(element, expression, index) {
    return Selector.findElement($(element).previousSiblings(), expression, index);
  },

  next: function(element, expression, index) {
    return Selector.findElement($(element).nextSiblings(), expression, index);
  },

  getElementsBySelector: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  getElementsByClassName: function(element, className) {
    return document.getElementsByClassName(className, element);
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (document.all && !window.opera) {
      var t = Element._attributeTranslations;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name])  name = t.names[name];
      var attribute = element.attributes[name];
      if(attribute) return attribute.nodeValue;
    }
    return element.getAttribute(name);
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    if (elementClassName.length == 0) return false;
    if (elementClassName == className ||
        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      return true;
    return false;
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element).add(className);
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element).remove(className);
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
    return element;
  },

  observe: function() {
    Event.observe.apply(Event, arguments);
    return $A(arguments).first();
  },

  stopObserving: function() {
    Event.stopObserving.apply(Event, arguments);
    return $A(arguments).first();
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.match(/^\s*$/);
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);
    while (element = element.parentNode)
      if (element == ancestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = Position.cumulativeOffset(element);
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    if (['float','cssFloat'].include(style))
      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
    style = style.camelize();
    var value = element.style[style];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css[style] : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style];
      }
    }

    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
      value = element['offset'+style.capitalize()] + 'px';

    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
    if(style == 'opacity') {
      if(value) return parseFloat(value);
      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if(value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }
    return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
    element = $(element);
    for (var name in style) {
      var value = style[name];
      if(name == 'opacity') {
        if (value == 1) {
          value = (/Gecko/.test(navigator.userAgent) &&
            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
        } else if(value === '') {
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
        } else {
          if(value < 0.00001) value = 0;
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
              'alpha(opacity='+value*100+')';
        }
      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
      element.style[name.camelize()] = value;
    }
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = $(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = element.style.overflow || 'auto';
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  }
};

Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});

Element._attributeTranslations = {};

Element._attributeTranslations.names = {
  colspan:   "colSpan",
  rowspan:   "rowSpan",
  valign:    "vAlign",
  datetime:  "dateTime",
  accesskey: "accessKey",
  tabindex:  "tabIndex",
  enctype:   "encType",
  maxlength: "maxLength",
  readonly:  "readOnly",
  longdesc:  "longDesc"
};

Element._attributeTranslations.values = {
  _getAttr: function(element, attribute) {
    return element.getAttribute(attribute, 2);
  },

  _flag: function(element, attribute) {
    return $(element).hasAttribute(attribute) ? attribute : null;
  },

  style: function(element) {
    return element.style.cssText.toLowerCase();
  },

  title: function(element) {
    var node = element.getAttributeNode('title');
    return node.specified ? node.nodeValue : null;
  }
};

Object.extend(Element._attributeTranslations.values, {
  href: Element._attributeTranslations.values._getAttr,
  src:  Element._attributeTranslations.values._getAttr,
  disabled: Element._attributeTranslations.values._flag,
  checked:  Element._attributeTranslations.values._flag,
  readonly: Element._attributeTranslations.values._flag,
  multiple: Element._attributeTranslations.values._flag
});

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    var t = Element._attributeTranslations;
    attribute = t.names[attribute] || attribute;
    return $(element).getAttributeNode(attribute).specified;
  }
};

// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
  Element.Methods.update = function(element, html) {
    element = $(element);
    html = typeof html == 'undefined' ? '' : html.toString();
    var tagName = element.tagName.toUpperCase();
    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
      var div = document.createElement('div');
      switch (tagName) {
        case 'THEAD':
        case 'TBODY':
          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
          depth = 2;
          break;
        case 'TR':
          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
          depth = 3;
          break;
        case 'TD':
          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
          depth = 4;
      }
      $A(element.childNodes).each(function(node){
        element.removeChild(node)
      });
      depth.times(function(){ div = div.firstChild });

      $A(div.childNodes).each(
        function(node){ element.appendChild(node) });
    } else {
      element.innerHTML = html.stripScripts();
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  }
};

Object.extend(Element, Element.Methods);

var _nativeExtensions = false;

if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
    var className = 'HTML' + tag + 'Element';
    if(window[className]) return;
    var klass = window[className] = {};
    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
  });

Element.addMethods = function(methods) {
  Object.extend(Element.Methods, methods || {});

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    var cache = Element.extend.cache;
    for (var property in methods) {
      var value = methods[property];
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = cache.findOrStore(value);
    }
  }

  if (typeof HTMLElement != 'undefined') {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
    copy(Form.Methods, HTMLFormElement.prototype);
    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
      copy(Form.Element.Methods, klass.prototype);
    });
    _nativeExtensions = true;
  }
}

var Toggle = new Object();
Toggle.display = Element.toggle;

/*--------------------------------------------------------------------------*/

Abstract.Insertion = function(adjacency) {
  this.adjacency = adjacency;
}

Abstract.Insertion.prototype = {
  initialize: function(element, content) {
    this.element = $(element);
    this.content = content.stripScripts();

    if (this.adjacency && this.element.insertAdjacentHTML) {
      try {
        this.element.insertAdjacentHTML(this.adjacency, this.content);
      } catch (e) {
        var tagName = this.element.tagName.toUpperCase();
        if (['TBODY', 'TR'].include(tagName)) {
          this.insertContent(this.contentFromAnonymousTable());
        } else {
          throw e;
        }
      }
    } else {
      this.range = this.element.ownerDocument.createRange();
      if (this.initializeRange) this.initializeRange();
      this.insertContent([this.range.createContextualFragment(this.content)]);
    }

    setTimeout(function() {content.evalScripts()}, 10);
  },

  contentFromAnonymousTable: function() {
    var div = document.createElement('div');
    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    return $A(div.childNodes[0].childNodes[0].childNodes);
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  initializeRange: function() {
    this.range.setStartBefore(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment, this.element);
    }).bind(this));
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(true);
  },

  insertContent: function(fragments) {
    fragments.reverse(false).each((function(fragment) {
      this.element.insertBefore(fragment, this.element.firstChild);
    }).bind(this));
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.appendChild(fragment);
    }).bind(this));
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  initializeRange: function() {
    this.range.setStartAfter(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment,
        this.element.nextSibling);
    }).bind(this));
  }
});

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);
var Selector = Class.create();
Selector.prototype = {
  initialize: function(expression) {
    this.params = {classNames: []};
    this.expression = expression.toString().strip();
    this.parseExpression();
    this.compileMatcher();
  },

  parseExpression: function() {
    function abort(message) { throw 'Parse error in selector: ' + message; }

    if (this.expression == '')  abort('empty expression');

    var params = this.params, expr = this.expression, match, modifier, clause, rest;
    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
      params.attributes = params.attributes || [];
      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
      expr = match[1];
    }

    if (expr == '*') return this.params.wildcard = true;

    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
      modifier = match[1], clause = match[2], rest = match[3];
      switch (modifier) {
        case '#':       params.id = clause; break;
        case '.':       params.classNames.push(clause); break;
        case '':
        case undefined: params.tagName = clause.toUpperCase(); break;
        default:        abort(expr.inspect());
      }
      expr = rest;
    }

    if (expr.length > 0) abort(expr.inspect());
  },

  buildMatchExpression: function() {
    var params = this.params, conditions = [], clause;

    if (params.wildcard)
      conditions.push('true');
    if (clause = params.id)
      conditions.push('element.readAttribute("id") == ' + clause.inspect());
    if (clause = params.tagName)
      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
    if ((clause = params.classNames).length > 0)
      for (var i = 0, length = clause.length; i < length; i++)
        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
    if (clause = params.attributes) {
      clause.each(function(attribute) {
        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
        var splitValueBy = function(delimiter) {
          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
        }

        switch (attribute.operator) {
          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
          case '|=':      conditions.push(
                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
                          ); break;
          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
          case '':
          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
        }
      });
    }

    return conditions.join(' && ');
  },

  compileMatcher: function() {
    this.match = new Function('element', 'if (!element.tagName) return false; \
      element = $(element); \
      return ' + this.buildMatchExpression());
  },

  findElements: function(scope) {
    var element;

    if (element = $(this.params.id))
      if (this.match(element))
        if (!scope || Element.childOf(element, scope))
          return [element];

    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');

    var results = [];
    for (var i = 0, length = scope.length; i < length; i++)
      if (this.match(element = scope[i]))
        results.push(Element.extend(element));

    return results;
  },

  toString: function() {
    return this.expression;
  }
}

Object.extend(Selector, {
  matchElements: function(elements, expression) {
    var selector = new Selector(expression);
    return elements.select(selector.match.bind(selector)).map(Element.extend);
  },

  findElement: function(elements, expression, index) {
    if (typeof expression == 'number') index = expression, expression = false;
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    return expressions.map(function(expression) {
      return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
        var selector = new Selector(expr);
        return results.inject([], function(elements, result) {
          return elements.concat(selector.findElements(result || element));
        });
      });
    }).flatten();
  }
});

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, getHash) {
    var data = elements.inject({}, function(result, element) {
      if (!element.disabled && element.name) {
        var key = element.name, value = $(element).getValue();
        if (value != undefined) {
          if (result[key]) {
            if (result[key].constructor != Array) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return getHash ? data : Hash.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, getHash) {
    return Form.serializeElements(Form.getElements(form), getHash);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    form.getElements().each(function(element) {
      element.blur();
      element.disabled = 'true';
    });
    return form;
  },

  enable: function(form) {
    form = $(form);
    form.getElements().each(function(element) {
      element.disabled = '';
    });
    return form;
  },

  findFirstElement: function(form) {
    return $(form).getElements().find(function(element) {
      return element.type != 'hidden' && !element.disabled &&
        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  }
}

Object.extend(Form, Form.Methods);

/*--------------------------------------------------------------------------*/

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
}

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = {};
        pair[element.name] = value;
        return Hash.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    element.focus();
    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
      !['button', 'reset', 'submit'].include(element.type) ) )
      element.select();
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.blur();
    element.disabled = false;
    return element;
  }
}

Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;
var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
      default:
        return Form.Element.Serializers.textarea(element);
    }
  },

  inputSelector: function(element) {
    return element.checked ? element.value : null;
  },

  textarea: function(element) {
    return element.value;
  },

  select: function(element) {
    return this[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
}

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
  initialize: function(element, frequency, callback) {
    this.frequency = frequency;
    this.element   = $(element);
    this.callback  = callback;

    this.lastValue = this.getValue();
    this.registerCallback();
  },

  registerCallback: function() {
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  onTimerEvent: function() {
    var value = this.getValue();
    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
      ? this.lastValue != value : String(this.lastValue) != String(value));
    if (changed) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
}

Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback.bind(this));
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
}

Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,

  element: function(event) {
    return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
    return (((event.which) && (event.which == 1)) ||
            ((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
    return event.pageX || (event.clientX +
      (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
    return event.pageY || (event.clientY +
      (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
    if (event.preventDefault) {
      event.preventDefault();
      event.stopPropagation();
    } else {
      event.returnValue = false;
      event.cancelBubble = true;
    }
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
    var element = Event.element(event);
    while (element.parentNode && (!element.tagName ||
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
      element = element.parentNode;
    return element;
  },

  observers: false,

  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },

  unloadCache: function() {
    if (!Event.observers) return;
    for (var i = 0, length = Event.observers.length; i < length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

    Event._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.detachEvent))
      name = 'keydown';

    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      try {
        element.detachEvent('on' + name, observer);
      } catch (e) {}
    }
  }
});

/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
  Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  realOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return [valueL, valueT];
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if(element.tagName=='BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  },

  offsetParent: function(element) {
    if (element.offsetParent) return element.offsetParent;
    if (element == document.body) return element;

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return element;

    return document.body;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = this.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = this.realOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = this.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  page: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent==document.body)
        if (Element.getStyle(element,'position')=='absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!window.opera || element.tagName=='BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return [valueL, valueT];
  },

  clone: function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || {})

    // find page position of source
    source = $(source);
    var p = Position.page(source);

    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  },

  absolutize: function(element) {
    element = $(element);
    if (element.style.position == 'absolute') return;
    Position.prepare();

    var offsets = Position.positionedOffset(element);
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
  },

  relativize: function(element) {
    element = $(element);
    if (element.style.position == 'relative') return;
    Position.prepare();

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
  }
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  Position.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return [valueL, valueT];
  }
}

Element.addMethods();

// Copyright (c) 2006 SĂŠbastien Gruhier (http://xilinus.com, http://itseb.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// VERSION 0.96.2

var Window = Class.create();
Window.prototype = {
  // Constructor
  // Available parameters : className, title, minWidth, minHeight, maxWidth, maxHeight, width, height, top, left, bottom, right, resizable, zIndex, opacity,
  //                        hideEffect, showEffect, showEffectOptions, hideEffectOptions, effectOptions, url, draggable, closable, minimizable, maximizable, parent, onload
  initialize: function(id) {
    if ($(id))
      alert("Window " + id + " is already register is the DOM!!, be sure to use setDestroyOnClose()")

    this.hasEffectLib = String.prototype.parseColor != null;
    this.options = Object.extend({
      className:         "dialog",
      minWidth:          100,
      minHeight:         20,
      resizable:         true,
      closable:          true,
      minimizable:       true,
      maximizable:       true,
      draggable:         true,
      userData:          null,
      showEffect:        (this.hasEffectLib ? Effect.Appear : Element.show),
      hideEffect:        (this.hasEffectLib ? Effect.Fade : Element.hide),
      showEffectOptions: {},
      hideEffectOptions: {},
      effectOptions:     null,
      parent:            document.getElementsByTagName("body").item(0),
      title:             "&nbsp;",
      url:               null,
      onload:            Prototype.emptyFunction,
      width:             200,
      height:            300,
      opacity:           1
    }, arguments[1] || {});

    if (this.options.effectOptions) {
      Object.extend(this.options.hideEffectOptions, this.options.effectOptions);
      Object.extend(this.options.showEffectOptions, this.options.effectOptions);
    }
    if (this.options.hideEffect == Element.hide)
      this.options.hideEffect = function(){ Element.hide(this.element); if (this.destroyOnClose) this.destroy(); }.bind(this)

    this.element = this._createWindow(id);

    // Bind event listener
    this.eventMouseDown = this._initDrag.bindAsEventListener(this);
    this.eventMouseUp   = this._endDrag.bindAsEventListener(this);
    this.eventMouseMove = this._updateDrag.bindAsEventListener(this);
    this.eventKeyPress  = this._keyPress.bindAsEventListener(this);
    this.eventOnLoad    = this._getWindowBorderSize.bindAsEventListener(this);
    this.eventMouseDownContent = this.toFront.bindAsEventListener(this);
    this.eventResize = this._recenter.bindAsEventListener(this);

    this.topbar = $(this.element.id + "_top");
    this.bottombar = $(this.element.id + "_bottom");
    this.content = $(this.element.id + "_content");

    Event.observe(this.topbar, "mousedown", this.eventMouseDown);
//    Event.observe(this.bottombar, "mousedown", this.eventMouseDown);
    Event.observe(this.content, "mousedown", this.eventMouseDownContent);
    Event.observe(window, "load", this.eventOnLoad);
    Event.observe(window, "resize", this.eventResize);
    Event.observe(window, "scroll", this.eventResize);

    if (this.options.draggable)  {
      this.bottombar.addClassName("bottom_draggable");
      this.topbar.addClassName("top_draggable");
    }

    if (this.options.resizable) {
      this.sizer = $(this.element.id + "_sizer");
      Event.observe(this.sizer, "mousedown", this.eventMouseDown);
    }

    this.useLeft = null;
    this.useTop = null;
    if (arguments[1].left != null) {
      this.element.setStyle({left: parseFloat(arguments[1].left) + 'px'});
      this.useLeft = true;
    }
    if (arguments[1].right != null) {
      this.element.setStyle({right: parseFloat(arguments[1].right) + 'px'});
      this.useLeft = false;
    }
    if (this.useLeft == null) {
      this.element.setStyle({left: "0px"});
      this.useLeft = true;
    }

    if (arguments[1].top != null) {
      this.element.setStyle({top: parseFloat(arguments[1].top) + 'px'});
      this.useTop = true;
    }
    if (arguments[1].bottom != null) {
      this.element.setStyle({bottom: parseFloat(arguments[1].bottom) + 'px'});
      this.useTop = false;
    }
    if (this.useTop == null) {
      this.element.setStyle({top: "0px"});
      this.useTop = true;
    }

    this.storedLocation = null;

    this.setOpacity(this.options.opacity);
    if (this.options.zIndex)
      this.setZIndex(this.options.zIndex)

    this.destroyOnClose = false;

    this._getWindowBorderSize();
    this.width = this.options.width;
    this.height = this.options.height;

    if (this.width && this.height)
      this.setSize(this.options.width, this.options.height);
    this.setTitle(this.options.title)
    Windows.register(this);
  },

  // Destructor
  destroy: function() {
    Windows.notify("onDestroy", this);

    Event.stopObserving(this.topbar, "mousedown", this.eventMouseDown);
    Event.stopObserving(this.bottombar, "mousedown", this.eventMouseDown);
    Event.stopObserving(this.content, "mousedown", this.eventMouseDownContent);

    Event.stopObserving(window, "load", this.eventOnLoad);
    Event.stopObserving(window, "resize", this.eventResize);
    Event.stopObserving(window, "scroll", this.eventResize);

    Event.stopObserving(this.content, "load", this.options.onload);

    if (this.sizer)
        Event.stopObserving(this.sizer, "mousedown", this.eventMouseDown);

    if (this.options.url)
      this.content.src = null

    if(this.iefix)
      Element.remove(this.iefix);

    Element.remove(this.element);
    Windows.unregister(this);
  },

  // Sets window deleagte, should have functions: "canClose(window)"
  setDelegate: function(delegate) {
    this.delegate = delegate
  },

  // Gets current window delegate
  getDelegate: function() {
    return this.delegate;
  },

  // Gets window content
  getContent: function () {
    return this.content;
  },

  // Sets the content with an element id
  setContent: function(id, autoresize, autoposition) {
    var d = null;
    var p = null;

    if (autoresize)
      d = Element.getDimensions(id);
    if (autoposition)
      p = Position.cumulativeOffset($(id));

    var content = this.getContent()
    content.appendChild($(id));
    $(id).show();
    if (autoresize)
      this.setSize(d.width, d.height);
    if (autoposition)
      this.setLocation(p[1] - this.heightN, p[0] - this.widthW);
  },

  setAjaxContent: function(url, options, showCentered, showModal) {
    this.showFunction = showCentered ? "showCenter" : "show";
    this.showModal = showModal || false;

    if (options == null)
      options = {}
    this.onComplete = options.onComplete;
    options.onComplete = this._setAjaxContent.bind(this);

      urchinTracker(url);
    new Ajax.Request(url, options);
  },

  _setAjaxContent: function(originalRequest) {
    this.getContent().innerHTML = originalRequest.responseText;

    // execute any scripts in content
    originalRequest.responseText.evalScripts();

    if (this.onComplete)
      this.onComplete(originalRequest);
    this[this.showFunction](this.showModal)
  },

  // Stores position/size in a cookie, by default named with window id
  setCookie: function(name, expires, path, domain, secure) {
    name = name || this.element.id;
    this.cookie = [name, expires, path, domain, secure];

    // Get cookie
    var value = WindowUtilities.getCookie(name)
    // If exists
    if (value) {
      var values = value.split(',');
      var x = values[0].split(':');
      var y = values[1].split(':');

      var w = parseFloat(values[2]), h = parseFloat(values[3]);
      var mini = values[4];
      var maxi = values[5];

      this.setSize(w, h);
      if (mini == "true")
        this.doMinimize = true; // Minimize will be done at onload window event
      else if (maxi == "true")
        this.doMaximize = true; // Maximize will be done at onload window event

      this.useLeft = x[0] == "l";
      this.useTop = y[0] == "t";

      this.element.setStyle(this.useLeft ? {left: x[1]} : {right: x[1]});
      this.element.setStyle(this.useTop ? {top: y[1]} : {bottom: y[1]});
    }
  },

  // Gets window ID
  getId: function() {
    return this.element.id;
  },

  // Detroys itself when closing
  setDestroyOnClose: function() {
    Object.extend(this.options.hideEffectOptions, {afterFinish:  this.destroy.bind(this)});
    this.destroyOnClose = true;
  },

  // initDrag event
  _initDrag: function(event) {
    // Get pointer X,Y
    this.pointer = [Event.pointerX(event), Event.pointerY(event)];

    // Resize
    if (Event.element(event) == this.sizer) {
      this.doResize = true;
      this.widthOrg = this.width;
      this.heightOrg = this.height;
      this.bottomOrg = parseFloat(this.element.getStyle('bottom'));
      this.rightOrg = parseFloat(this.element.getStyle('right'));
      Windows.notify("onStartResize", this);
    }
    else {
      this.doResize = false;

      // Check if click on close button,
      var closeButton = $(this.getId() + '_close');
      if (closeButton && Position.within(closeButton, this.pointer[0], this.pointer[1]))
        return;

      this.toFront();

      if (! this.options.draggable)
        return;
      Windows.notify("onStartMove", this);
    }
    // Register global event to capture mouseUp and mouseMove
    Event.observe(document, "mouseup", this.eventMouseUp, false);
    Event.observe(document, "mousemove", this.eventMouseMove, false);

    // Add an invisible div to keep catching mouse event over iframes
    WindowUtilities.disableScreen('__invisible__', '__invisible__');

    // Stop selection while dragging
    document.body.ondrag = function () { return false; };
    document.body.onselectstart = function () { return false; };

    Event.stop(event);
  },

  // updateDrag event
  _updateDrag: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var dx = pointer[0] - this.pointer[0];
    var dy = pointer[1] - this.pointer[1];

    // Resize case, update width/height
    if (this.doResize) {
      this.setSize(this.widthOrg + dx , this.heightOrg + dy);

      dx = this.width - this.widthOrg
      dy = this.height - this.heightOrg

      // Check if it's a right position, update it to keep upper-left corner at the same position
      if (! this.useLeft)
        this.element.setStyle({right: (this.rightOrg -dx) + 'px'});
      // Check if it's a bottom position, update it to keep upper-left corner at the same position
      if (! this.useTop)
        this.element.setStyle({bottom: (this.bottomOrg -dy) + 'px'});
    }
    // Move case, update top/left
    else {
      this.pointer = pointer;

      if (this.useLeft)
        this.element.setStyle({left: parseFloat(this.element.getStyle('left')) + dx + 'px'});
      else
        this.element.setStyle({right: parseFloat(this.element.getStyle('right')) - dx + 'px'});

      if (this.useTop)
        this.element.setStyle({top: parseFloat(this.element.getStyle('top')) + dy + 'px'});
      else
        this.element.setStyle({bottom: parseFloat(this.element.getStyle('bottom')) - dy + 'px'});
    }
    if (this.iefix)
      this._fixIEOverlapping();

    this._removeStoreLocation();
    Event.stop(event);
  },

   // endDrag callback
  _endDrag: function(event) {
    // Remove temporary div over iframes
    WindowUtilities.enableScreen('__invisible__');

    if (this.doResize)
      Windows.notify("onEndResize", this);
    else
      Windows.notify("onEndMove", this);

    // Release event observing
    Event.stopObserving(document, "mouseup", this.eventMouseUp,false);
    Event.stopObserving(document, "mousemove", this.eventMouseMove, false);

    // Store new location/size if need be
    this._saveCookie()

    Event.stop(event);

    // Restore selection
    document.body.ondrag = null;
    document.body.onselectstart = null;
  },

  _keyPress: function(event) {
    //Dialog.cancelCallback();
  },

  // Creates HTML window code
  _createWindow: function(id) {
    var className = this.options.className;
    var win = document.createElement("div");
    win.setAttribute('id', id);
    win.className = "dialog";

    var content;
    if (this.options.url)
      content= "<iframe name=\"" + id + "_content\"  id=\"" + id + "_content\" src=\"" + this.options.url + "\"> </iframe>";
    else
      content ="<div id=\"" + id + "_content\" class=\"" +className + "_content\"> </div>";

    var closeDiv = this.options.closable ? "<div class='"+ className +"_close' id='"+ id +"_close' onmouseup='Windows.close(\""+ id +"\")'>&nbsp;close</div>" : "";
    var bottomCloseDiv = this.options.closable ? "<a href=# style='font-weight:bold;font-size:10pt;' onmouseup='Windows.close(\""+ id +"\")'>close</a>" : "";
    var minDiv = this.options.minimizable ? "<div class='"+ className + "_minimize' id='"+ id +"_minimize' onmouseup='Windows.minimize(\""+ id +"\")'> </div>" : "";
    var maxDiv = this.options.maximizable ? "<div class='"+ className + "_maximize' id='"+ id +"_maximize' onmouseup='Windows.maximize(\""+ id +"\")'> </div>" : "";
    var seAttributes = this.options.resizable ? "class='" + className + "_sizer' id='" + id + "_sizer'" : "class='"  + className + "_se'";

    win.innerHTML = closeDiv + minDiv + maxDiv + "\
      <table id='"+ id +"_row1' class=\"top table_window\">\
        <tr>\
          <td class='"+ className +"_nw'>&nbsp;</td>\
          <td class='"+ className +"_n'><div id='"+ id +"_top' class='"+ className +"_title title_window'>"+ this.options.title +"</div></td>\
          <td class='"+ className +"_ne'>&nbsp;</td>\
        </tr>\
      </table>\
      <table id='"+ id +"_row2' class=\"mid table_window\">\
        <tr>\
          <td class='"+ className +"_w'></td>\
            <td id='"+ id +"_table_content' class='"+ className +"_content' valign='top'>"+ content +"</td>\
          <td class='"+ className +"_e'></td>\
        </tr>\
      </table>\
        <table id='"+ id +"_row3' class=\"bot table_window\">\
        <tr>\
          <td class='"+ className +"_sw'>&nbsp;</td>\
            <td class='"+ className +"_s'><div id='"+ id +"_bottom' class='status_bar'>&nbsp;"+bottomCloseDiv+"</div></td>\
            <td " + seAttributes + ">&nbsp;</td>\
        </tr>\
      </table>\
    ";

    Element.hide(win);
    this.options.parent.insertBefore(win, this.options.parent.firstChild);
    Event.observe($(id + "_content"), "load", this.options.onload);
    return win;
  },

  // Sets window location
  setLocation: function(top, left) {
    if (top < 0)
      top = 0;
    if (left < 0)
      left= 0
    this.element.setStyle({top: top + 'px'});
    this.element.setStyle({left: left + 'px'});
    this.useLeft = true;
    this.useTop = true;
  },

  // Gets window size
  getSize: function() {
    return {width: this.width, height: this.height};
  },

  // Sets window size
  setSize: function(width, height) {
    width = parseFloat(width);
    height = parseFloat(height);

    // Check min and max size
    if (width < this.options.minWidth)
      width = this.options.minWidth;

    if (height < this.options.minHeight)
      height = this.options.minHeight;

    if (this.options. maxHeight && height > this.options. maxHeight)
      height = this.options. maxHeight;

    if (this.options. maxWidth && width > this.options. maxWidth)
      width = this.options. maxWidth;

    this.width = width;
    this.height = height;
    this.element.setStyle({width: width + this.widthW + this.widthE + "px"})
    this.element.setStyle({height: height  + this.heightN + this.heightS + "px"})

    // Update content height
    var content = $(this.element.id + '_content')
    content.setStyle({height: height  + 'px'});
    content.setStyle({width: width  + 'px'});
  },

  updateHeight: function() {
    this.setSize(this.width, this.content.scrollHeight)
  },

  updateWidth: function() {
    this.setSize(this.content.scrollWidth, this.height)
  },

  // Brings window to front
  toFront: function() {
    this.setZIndex(Windows.maxZIndex + 20);
    Windows.notify("onFocus", this);
  },

  // Displays window modal state or not
  show: function(modal) {
    if (modal) {
      WindowUtilities.disableScreen(this.options.className, 'overlay_modal', this.getId());
      this.modal = true;
      this.setZIndex(Windows.maxZIndex + 20);
      Windows.unsetOverflow(this);
      Event.observe(document, "keypress", this.eventKeyPress);
    }

    // To restore overflow if need be
    if (this.oldStyle)
      this.getContent().setStyle({overflow: this.oldStyle});

    if (! this.width || !this.height) {
      var size = WindowUtilities._computeSize(this.content.innerHTML, this.content.id, this.width, this.height, 0)
      if (this.height)
        this.width = size + 5
      else
        this.height = size + 5
    }

    this.setSize(this.width, this.height);
    if (this.centered)
      this._center(this.centerTop, this.centerLeft);

    if (this.options.showEffect != Element.show && this.options.showEffectOptions )
      this.options.showEffect(this.element, this.options.showEffectOptions);
    else
      this.options.showEffect(this.element);

    this._checkIEOverlapping();
    Windows.notify("onShow", this);
  },

  // Displays window modal state or not at the center of the page
  showCenter: function(modal, top, left) {
    this.centered = true;
    this.centerTop = top;
    this.centerLeft = left;

    this.show(modal);
  },

  isVisible: function() {
    return this.element.visible();
  },

  _center: function(top, left) {
    var windowScroll = WindowUtilities.getWindowScroll();
    var pageSize = WindowUtilities.getPageSize();

    if (!top)
      top = (pageSize.windowHeight - (this.height + this.heightN + this.heightS))/2;
    top += windowScroll.top

    if (!left)
      left = (pageSize.windowWidth - (this.width + this.widthW + this.widthE))/2;
    left += windowScroll.left

    this.setLocation(top, left);
    this.toFront();
  },

  _recenter: function(event) {
    if (this.modal) {
      var pageSize = WindowUtilities.getPageSize();
      // set height of Overlay to take up whole page and show
      if ($('overlay_modal')) {
        $('overlay_modal').style.height = (pageSize.pageHeight + 'px');
        $('overlay_modal').style.width = (pageSize.pageWidth + 'px');
      }
      if (this.centered)
        this._center(this.centerTop, this.centerLeft);
    }
  },

  // Hides window
  hide: function() {
    if (this.modal) {
      WindowUtilities.enableScreen();
      Windows.resetOverflow();
      Event.stopObserving(document, "keypress", this.eventKeyPress);
    }
    // To avoid bug on scrolling bar
    this.oldStyle = this.getContent().getStyle('overflow') || "auto"
    this.getContent().setStyle({overflow: "hidden"});

    this.options.hideEffect(this.element, this.options.hideEffectOptions);

    if(this.iefix)
      this.iefix.hide();
    Windows.notify("onHide", this);
  },

  minimize: function() {
    var r2 = $(this.getId() + "_row2");
    var dh = r2.getDimensions().height;

    if (r2.visible()) {
      var h  = this.element.getHeight() - dh
      r2.hide()
      this.element.setStyle({height: h + "px"})
      if (! this.useTop) {
        var bottom = parseFloat(this.element.getStyle('bottom'));
        this.element.setStyle({bottom: (bottom + dh) + 'px'});
      }
    }
    else {
      var h  = this.element.getHeight() + dh;
      this.element.setStyle({height: h + "px"})
      if (! this.useTop) {
        var bottom = parseFloat(this.element.getStyle('bottom'));
        this.element.setStyle({bottom: (bottom - dh) + 'px'});
      }
      r2.show();

      this.toFront();
    }
    Windows.notify("onMinimize", this);

    // Store new location/size if need be
    this._saveCookie()
  },

  maximize: function() {
    if (this.storedLocation != null) {
      this._restoreLocation();
      if(this.iefix)
        this.iefix.hide();
    }
    else {
      this._storeLocation();
      Windows.unsetOverflow(this);

      var windowScroll = WindowUtilities.getWindowScroll();
      var pageSize = WindowUtilities.getPageSize();

      this.element.setStyle(this.useLeft ? {left: windowScroll.left} : {right: windowScroll.left});
      this.element.setStyle(this.useTop ? {top: windowScroll.top} : {bottom: windowScroll.top});

      this.setSize(pageSize.windowWidth - this.widthW - this.widthE, pageSize.windowHeight - this.heightN - this.heightS)
      this.toFront();
      if (this.iefix)
        this._fixIEOverlapping();
    }
    Windows.notify("onMaximize", this);

    // Store new location/size if need be
    this._saveCookie()
  },

  isMinimized: function() {
    var r2 = $(this.getId() + "_row2");
    return !r2.visible();
  },

  isMaximized: function() {
    return (this.storedLocation != null);
  },

  setOpacity: function(opacity) {
    if (Element.setOpacity)
      Element.setOpacity(this.element, opacity);
  },

  setZIndex: function(zindex) {
    this.element.setStyle({zIndex: zindex});
    Windows.updateZindex(zindex, this);
  },

  setTitle: function(newTitle) {
    if (!newTitle || newTitle == "")
      newTitle = "&nbsp;";

    Element.update(this.element.id + '_top', newTitle);
  },

  setStatusBar: function(element) {
    var statusBar = $(this.getId() + "_bottom");

    if (typeof(element) == "object") {
      if (this.bottombar.firstChild)
        this.bottombar.replaceChild(element, this.bottombar.firstChild);
      else
        this.bottombar.appendChild(element);
    }
    else
      this.bottombar.innerHTML = element;
  },

  _checkIEOverlapping: function() {
    if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (navigator.userAgent.indexOf('Opera')<0) && (this.element.getStyle('position')=='absolute')) {
        new Insertion.After(this.element.id, '<iframe id="' + this.element.id + '_iefix" '+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
        this.iefix = $(this.element.id+'_iefix');
    }
    if(this.iefix)
      setTimeout(this._fixIEOverlapping.bind(this), 50);
  },

  _fixIEOverlapping: function() {
      Position.clone(this.element, this.iefix);
      this.iefix.style.zIndex = this.element.style.zIndex - 1;
      this.iefix.show();
  },

  _getWindowBorderSize: function(event) {
    // Hack to get real window border size!!
    var div = this._createHiddenDiv(this.options.className + "_n")
    this.heightN = Element.getDimensions(div).height;
    div.parentNode.removeChild(div)

    var div = this._createHiddenDiv(this.options.className + "_s")
    this.heightS = Element.getDimensions(div).height;
    div.parentNode.removeChild(div)

    var div = this._createHiddenDiv(this.options.className + "_e")
    this.widthE = Element.getDimensions(div).width;
    div.parentNode.removeChild(div)

    var div = this._createHiddenDiv(this.options.className + "_w")
    this.widthW = Element.getDimensions(div).width;
    div.parentNode.removeChild(div);
    // Safari size fix
    if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
      this.setSize(this.width, this.height);
    if (this.doMaximize)
      this.maximize();
    if (this.doMinimize)
      this.minimize();
  },

  _createHiddenDiv: function(className) {
    var objBody = document.getElementsByTagName("body").item(0);
    var win = document.createElement("div");
    win.setAttribute('id', this.element.id+ "_tmp");
    win.className = className;
    win.style.display = 'none'
    win.innerHTML = ''
    objBody.insertBefore(win, objBody.firstChild)
    return win
  },

  _storeLocation: function() {
    if (this.storedLocation == null) {
      this.storedLocation = {useTop: this.useTop, useLeft: this.useLeft,
                             top: this.element.getStyle('top'), bottom: this.element.getStyle('bottom'),
                             left: this.element.getStyle('left'), right: this.element.getStyle('right'),
                             width: this.width, height: this.height };
    }
  },

  _restoreLocation: function() {
    if (this.storedLocation != null) {
      this.useLeft = this.storedLocation.useLeft;
      this.useTop = this.storedLocation.useTop;

      this.element.setStyle(this.useLeft ? {left: this.storedLocation.left} : {right: this.storedLocation.right});
      this.element.setStyle(this.useTop ? {top: this.storedLocation.top} : {bottom: this.storedLocation.bottom});
      this.setSize(this.storedLocation.width, this.storedLocation.height);

      Windows.resetOverflow();
      this._removeStoreLocation();
    }
  },

  _removeStoreLocation: function() {
    this.storedLocation = null;
  },

  _saveCookie: function() {
    if (this.cookie) {
      var value = "";
      if (this.useLeft)
        value += "l:" +  (this.storedLocation ? this.storedLocation.left : this.element.getStyle('left'))
      else
        value += "r:" + (this.storedLocation ? this.storedLocation.right : this.element.getStyle('right'))
      if (this.useTop)
        value += ",t:" + (this.storedLocation ? this.storedLocation.top : this.element.getStyle('top'))
      else
        value += ",b:" + (this.storedLocation ? this.storedLocation.bottom :this.element.getStyle('bottom'))

      value += "," + (this.storedLocation ? this.storedLocation.width : this.width);
      value += "," + (this.storedLocation ? this.storedLocation.height : this.height);
      value += "," + this.isMinimized();
      value += "," + this.isMaximized();
      WindowUtilities.setCookie(value, this.cookie)
    }
  }
};

// Windows containers, register all page windows
var Windows = {
  windows: [],
  observers: [],
  focusedWindow: null,
  maxZIndex: 0,

  addObserver: function(observer) {
    this.removeObserver(observer);
    this.observers.push(observer);
  },

  removeObserver: function(observer) {
    this.observers = this.observers.reject( function(o) { return o==observer });
  },

  notify: function(eventName, win) {  //  onStartResize(), onEndResize(), onStartMove(), onEndMove(), onClose(), onDestroy(), onMinimize(), onMaximize(), onHide(), onShow(), onFocus()
    this.observers.each( function(o) {if(o[eventName]) o[eventName](eventName, win);});
  },

  // Gets window from its id
  getWindow: function(id) {
    return this.windows.detect(function(d) { return d.getId() ==id });
  },

  // Gets the last focused window
  getFocusedWindow: function() {
    return this.focusedWindow;
  },

  // Registers a new window (called by Windows constructor)
  register: function(win) {
    this.windows.push(win);
  },

  // Unregisters a window (called by Windows destructor)
  unregister: function(win) {
    this.windows = this.windows.reject(function(d) { return d==win });
  },

  // Closes a window with its id
  close: function(id) {
    var win = this.getWindow(id);
    // Asks delegate if exists
    if (win) {
      if (win.getDelegate() && ! win.getDelegate().canClose(win))
        return;
        if ($(id + "_close"))
          $(id + "_close").onclick = null;
        if ($(id + "_minimize"))
          $(id + "_minimize").onclick = null;
        if ($(id + "_maximize"))
          $(id + "_maximize").onclick = null;

        this.notify("onClose", win);
        win.hide();
    }
  },

  // Closes all windows
  closeAll: function() {
    this.windows.each( function(w) {Windows.close(w.getId())} );
  },

  // Minimizes a window with its id
  minimize: function(id) {
    var win = this.getWindow(id)
    if (win)
      win.minimize();
  },

  // Maximizes a window with its id
  maximize: function(id) {
    var win = this.getWindow(id)
    if (win)
      win.maximize();
  },

  unsetOverflow: function(except) {
    this.windows.each(function(d) { d.oldOverflow = d.getContent().getStyle("overflow") || "auto" ; d.getContent().setStyle({overflow: "hidden"}) });
    if (except && except.oldOverflow)
      except.getContent().setStyle({overflow: except.oldOverflow});
  },

  resetOverflow: function() {
    this.windows.each(function(d) { if (d.oldOverflow) d.getContent().setStyle({overflow: d.oldOverflow}) });
  },

  updateZindex: function(zindex, win) {
    if (zindex > this.maxZIndex)
      this.maxZIndex = zindex;
    this.focusedWindow = win;
  }
};

var Dialog = {
  dialogId: null,
  win: null,
  onCompleteFunc: null,
  callFunc: null,
  parameters: null,

  window: function(content, parameters) {
    // Get Ajax return before
    if (typeof content != "string") {
      Dialog._runAjaxRequest(content, parameters, Dialog.window);
      return
    }

    parameters = parameters || {};

    var windowParam = parameters.windowParameters || {};
    windowParam.className = windowParam.className || "alert";

    if (typeof(windowParam.subtitle) == "undefined") windowParam.subtitle = "";

    var content = "\
      <div style='clear:both;'><div style='float:left;text-align:center;font-size:10pt;'><b>"+windowParam.subtitle+"</b></div><div style='float:right;'><a href=# onclick='Dialog.okCallback();return false;'><img src='/i/images/16x16_delete.gif' style='border:0px solid;' title='Close'/> [close]</a>&nbsp;</div>\
      <div style='clear:both;' class='" + windowParam.className + "_message'>" + content  + "</div>\
    ";
  parameters.windowParameters.ok = null;
  parameters.windowParameters.cancel = null;

    this._openDialog(content, parameters)
    return this.win
  },

  confirm: function(content, parameters) {
    // Get Ajax return before
    if (typeof content != "string") {
      Dialog._runAjaxRequest(content, parameters, Dialog.confirm);
      return
    }

    parameters = parameters || {};
    var okLabel = parameters.okLabel ? parameters.okLabel : "Ok";
    var cancelLabel = parameters.cancelLabel ? parameters.cancelLabel : "Cancel";

    var windowParam = parameters.windowParameters || {};
    windowParam.className = windowParam.className || "alert";

    okButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " ok_button'"
    cancelButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " cancel_button'"
    var content = "\
      <div class='" + windowParam.className + "_message'>" + content  + "</div>\
        <div class='" + windowParam.className + "_buttons'>\
          <input type='button' value='" + okLabel + "' onclick='Dialog.okCallback()'" + okButtonClass + "/>\
          <input type='button' value='" + cancelLabel + "' onclick='Dialog.cancelCallback()' " + cancelButtonClass + "/>\
        </div>\
    ";
    this._openDialog(content, parameters)
    return this.win
  },

  alert: function(content, parameters) {
    // Get Ajax return before
    if (typeof content != "string") {
      Dialog._runAjaxRequest(content, parameters, Dialog.alert);
      return
    }

    parameters = parameters || {};
    var okLabel = parameters.okLabel ? parameters.okLabel : "Ok";

    var windowParam = parameters.windowParameters || {};
    windowParam.className = windowParam.className || "alert";

    okButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " ok_button'"
    var content = "\
      <div class='" + windowParam.className + "_message'>" + content  + "</div>\
        <div class='" + windowParam.className + "_buttons'>\
          <input type='button' value='" + okLabel + "' onclick='Dialog.okCallback()'" + okButtonClass + "/>\
        </div>";
    return this._openDialog(content, parameters)
  },

  info: function(content, parameters) {
    // Get Ajax return before
    if (typeof content != "string") {
      Dialog._runAjaxRequest(content, parameters, Dialog.info);
      return
    }

    parameters = parameters || {};
    parameters.windowParameters = parameters.windowParameters || {};

    var className = parameters.windowParameters.className || "alert";

    var content = "<div id='modal_dialog_message' class='" + className + "_message'>" + content  + "</div>";
    if (parameters.showProgress)
      content += "<div id='modal_dialog_progress' class='" + className + "_progress'> </div>";

    parameters.windowParameters.ok = null;
    parameters.windowParameters.cancel = null;
    parameters.windowParameters.className = className;

    return this._openDialog(content, parameters)
  },

  setInfoMessage: function(message) {
    $('modal_dialog_message').update(message);
  },

  closeInfo: function() {
    Windows.close(this.dialogId);
  },

  _openDialog: function(content, parameters) {
    // remove old dialog
    if (this.win)
      this.win.destroy();

    if (! parameters.windowParameters.height && ! parameters.windowParameters.width) {
      parameters.windowParameters.width = WindowUtilities.getPageSize().pageWidth / 2;
    }
    this.dialogId = parameters.id ? parameters.id : 'modal_dialog'

    // compute height or width if need be
    if (! parameters.windowParameters.height || ! parameters.windowParameters.width) {
      var size = WindowUtilities._computeSize(content, this.dialogId, parameters.windowParameters.width, parameters.windowParameters.height)
      if (parameters.windowParameters.height)
        parameters.windowParameters.width = size + 5
      else
        parameters.windowParameters.height = size + 5
    }
    var windowParam = parameters && parameters.windowParameters ? parameters.windowParameters : {};
    windowParam.resizable = windowParam.resizable || false;

    windowParam.effectOptions = windowParam.effectOptions || {duration: 1};
    windowParam.minimizable = false;
    windowParam.maximizable = false;
    windowParam.closable = false;
    this.win = new Window(this.dialogId, windowParam);
    this.win.getContent().innerHTML = content;
    this.win.showCenter(true, parameters.top, parameters.left);

    this.win.cancelCallback = parameters.cancel;
    this.win.okCallback = parameters.ok;

    // execute any scripts in content
    content.evalScripts();

    return this.win;
  },

  _getAjaxContent: function(originalRequest)  {
      Dialog.callFunc(originalRequest.responseText, Dialog.parameters)
  },

  _runAjaxRequest: function(message, parameters, callFunc) {
    if (message.options == null)
      message.options ={}
    Dialog.onCompleteFunc = message.options.onComplete;
    Dialog.parameters = parameters;
    Dialog.callFunc = callFunc;

    message.options.onComplete = Dialog._getAjaxContent;
    urchinTracker(message.url);
    new Ajax.Request(message.url, message.options);
  },

  okCallback: function() {
    if (!this.win.okCallback || this.win.okCallback(this.win))
      this.win.hide();
  },

  cancelCallback: function() {
    this.win.hide();
    if (this.win.cancelCallback)
      this.win.cancelCallback(this.win);
  }
}
/*
  Based on Lightbox JS: Fullsize Image Overlays
  by Lokesh Dhakar - http://www.huddletogether.com

  For more information on this script, visit:
  http://huddletogether.com/projects/lightbox/

  Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
  (basically, do anything you want, just leave my name and link)
*/

var isIE = navigator.appVersion.match(/MSIE/) == "MSIE";

var WindowUtilities = {
  // From script.aculo.us
  getWindowScroll: function() {
    var w = window;
      var T, L, W, H;
      with (w.document) {
        if (w.document.documentElement && documentElement.scrollTop) {
          T = documentElement.scrollTop;
          L = documentElement.scrollLeft;
        } else if (w.document.body) {
          T = body.scrollTop;
          L = body.scrollLeft;
        }
        if (w.innerWidth) {
          W = w.innerWidth;
          H = w.innerHeight;
        } else if (w.document.documentElement && documentElement.clientWidth) {
          W = documentElement.clientWidth;
          H = documentElement.clientHeight;
        } else {
          W = body.offsetWidth;
          H = body.offsetHeight
        }
      }
      return { top: T, left: L, width: W, height: H };

  },
  //
  // getPageSize()
  // Returns array with page width, height and window width, height
  // Core code from - quirksmode.org
  // Edit for Firefox by pHaez
  //
  getPageSize: function(){
    var xScroll, yScroll;

    if (window.innerHeight && window.scrollMaxY) {
      xScroll = document.body.scrollWidth;
      yScroll = window.innerHeight + window.scrollMaxY;
    } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
      xScroll = document.body.scrollWidth;
      yScroll = document.body.scrollHeight;
    } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
      xScroll = document.body.offsetWidth;
      yScroll = document.body.offsetHeight;
    }

    var windowWidth, windowHeight;

    if (self.innerHeight) { // all except Explorer
      windowWidth = self.innerWidth;
      windowHeight = self.innerHeight;
    } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
      windowWidth = document.documentElement.clientWidth;
      windowHeight = document.documentElement.clientHeight;
    } else if (document.body) { // other Explorers
      windowWidth = document.body.clientWidth;
      windowHeight = document.body.clientHeight;
    }
    var pageHeight, pageWidth;

    // for small pages with total height less then height of the viewport
    if(yScroll < windowHeight){
      pageHeight = windowHeight;
    } else {
      pageHeight = yScroll;
    }

    // for small pages with total width less then width of the viewport
    if(xScroll < windowWidth){
      pageWidth = windowWidth;
    } else {
      pageWidth = xScroll;
    }

    return {pageWidth: pageWidth ,pageHeight: pageHeight , windowWidth: windowWidth, windowHeight: windowHeight};
  },

  disableScreen: function(className, overlayId, contentId) {
    WindowUtilities.initLightbox(overlayId, className);
    var objBody = document.getElementsByTagName("body").item(0);

    // prep objects
    var objOverlay = $(overlayId);

    var pageSize = WindowUtilities.getPageSize();

    // Hide select boxes as they will 'peek' through the image in IE
    if (contentId && isIE) {
      $$('select').each(function(element) {element.style.visibility = "hidden"});
      $$('#'+contentId+' select').each(function(element) {element.style.visibility = "visible"});
    }

    // set height of Overlay to take up whole page and show
    objOverlay.style.height = (pageSize.pageHeight + 'px');
    objOverlay.style.width = (pageSize.windowWidth + 'px');
    objOverlay.style.display = 'block';

    if (isIE) {
       objOverlay.style.top = document.documentElement.scrollTop;
    }
  },

  enableScreen: function(id) {
    id = id || 'overlay_modal'
    var objOverlay =  $(id);
    if (objOverlay) {
      // hide lightbox and overlay
      objOverlay.style.display = 'none';

      // make select boxes visible
      if (isIE) {
        $$('select').each(function(element) {element.style.visibility = "visible"});
      }
      objOverlay.parentNode.removeChild(objOverlay);
    }
  },

  // initLightbox()
  // Function runs on window load, going through link tags looking for rel="lightbox".
  // These links receive onclick events that enable the lightbox display for their targets.
  // The function also inserts html markup at the top of the page which will be used as a
  // container for the overlay pattern and the inline image.
  initLightbox: function(id, className) {
    // Already done, just update zIndex
    if ($(id)) {
      Element.setStyle(id, {zIndex: Windows.maxZIndex + 10});
    }
    // create overlay div and hardcode some functional styles (aesthetic styles are in CSS file)
    else {
      var objBody = document.getElementsByTagName("body").item(0);
      var objOverlay = document.createElement("div");
      objOverlay.setAttribute('id', id);
      objOverlay.className = "overlay_" + className
      objOverlay.style.display = 'none';
      objOverlay.style.position = 'absolute';
      objOverlay.style.top = '0';
      objOverlay.style.left = '0';
      objOverlay.style.zIndex = Windows.maxZIndex + 10;
      objOverlay.style.width = '100%';
      objBody.insertBefore(objOverlay, objBody.firstChild);
    }
  },

  setCookie: function(value, parameters) {
    document.cookie= parameters[0] + "=" + escape(value) +
      ((parameters[1]) ? "; expires=" + parameters[1].toGMTString() : "") +
      ((parameters[2]) ? "; path=" + parameters[2] : "") +
      ((parameters[3]) ? "; domain=" + parameters[3] : "") +
      ((parameters[4]) ? "; secure" : "");
  },

  getCookie: function(name) {
    var dc = document.cookie;
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1) {
      begin = dc.indexOf(prefix);
      if (begin != 0) return null;
    } else {
      begin += 2;
    }
    var end = document.cookie.indexOf(";", begin);
    if (end == -1) {
      end = dc.length;
    }
    return unescape(dc.substring(begin + prefix.length, end));
  },

  _computeSize: function(content, id, width, height, margin) {
    if (margin == null)
      margin = 5;

    var objBody = document.getElementsByTagName("body").item(0);
    var tmpObj = document.createElement("div");
    tmpObj.setAttribute('id', id);

    if (height)
      tmpObj.style.height = height + "px"
    else
      tmpObj.style.width = width + "px"

    tmpObj.style.position = 'absolute';
    tmpObj.style.top = '0';
    tmpObj.style.left = '0';
    tmpObj.style.display = 'none';

    tmpObj.innerHTML = content;
    objBody.insertBefore(tmpObj, objBody.firstChild);

    var size;
    if (height)
      size = $(id).getDimensions().width + margin;
    else
      size = $(id).getDimensions().height + margin;
    objBody.removeChild(tmpObj);

    return size;
  }
}



// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007

// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/ 

// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var color = '#';
  if(this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
    if(this.slice(0,1) == '#') {  
      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
      if(this.length==7) color = this.toLowerCase();  
    }  
  }  
  return(color.length==7 ? color : (arguments[0] || this));  
}

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
}

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
}

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  element.setStyle({fontSize: (percent/100) + 'em'});   
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
  return element;
}

Element.getOpacity = function(element){
  return $(element).getStyle('opacity');
}

Element.setOpacity = function(element, value){
  return $(element).setStyle({opacity:value});
}

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
}

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

/*--------------------------------------------------------------------------*/

Array.prototype.call = function() {
  var args = arguments;
  this.each(function(f){ f.apply(this, args) });
}

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  tagifyText: function(element) {
    if(typeof Builder == 'undefined')
      throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
      
    var tagifyStyle = 'position:relative';
    if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
    
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if(child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            Builder.node('span',{style: tagifyStyle},
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if(((typeof element == 'object') || 
        (typeof element == 'function')) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || {});
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || {});
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

var Effect2 = Effect; // deprecated

/* ------------- transitions ------------- */

Effect.Transitions = {
  linear: Prototype.K,
  sinoidal: function(pos) {
    return (-Math.cos(pos*Math.PI)/2) + 0.5;
  },
  reverse: function(pos) {
    return 1-pos;
  },
  flicker: function(pos) {
    return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
  },
  wobble: function(pos) {
    return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
  },
  pulse: function(pos, pulses) { 
    pulses = pulses || 5; 
    return (
      Math.round((pos % (1/pulses)) * pulses) == 0 ? 
            ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
        1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
      );
  },
  none: function(pos) {
    return 0;
  },
  full: function(pos) {
    return 1;
  }
};

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
  initialize: function() {
    this.effects  = [];
    this.interval = null;
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = (typeof effect.options.queue == 'string') ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if(!this.interval) 
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if(this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++) 
      if(this.effects[i]) this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if(typeof queueName != 'string') return queueName;
    
    if(!this.instances[queueName])
      this.instances[queueName] = new Effect.ScopedQueue();
      
    return this.instances[queueName];
  }
}
Effect.Queue = Effect.Queues.get('global');

Effect.DefaultOptions = {
  transition: Effect.Transitions.sinoidal,
  duration:   1.0,   // seconds
  fps:        60.0,  // max. 60fps due to Effect.Queue implementation
  sync:       false, // true for combining
  from:       0.0,
  to:         1.0,
  delay:      0.0,
  queue:      'parallel'
}

Effect.Base = function() {};
Effect.Base.prototype = {
  position: null,
  start: function(options) {
    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn + (this.options.duration*1000);
    this.event('beforeStart');
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if(timePos >= this.startOn) {
      if(timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if(this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
      var frame = Math.round(pos * this.options.fps * this.options.duration);
      if(frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  render: function(pos) {
    if(this.state == 'idle') {
      this.state = 'running';
      this.event('beforeSetup');
      if(this.setup) this.setup();
      this.event('afterSetup');
    }
    if(this.state == 'running') {
      if(this.options.transition) pos = this.options.transition(pos);
      pos *= (this.options.to-this.options.from);
      pos += this.options.from;
      this.position = pos;
      this.event('beforeUpdate');
      if(this.update) this.update(pos);
      this.event('afterUpdate');
    }
  },
  cancel: function() {
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if(this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if(typeof this[property] != 'function') data[property] = this[property];
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
}

Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if(effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Event = Class.create();
Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
  initialize: function() {
    var options = Object.extend({
      duration: 0
    }, arguments[0] || {});
    this.start(options);
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom: 1});
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || {});
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Bug in Opera: Opera returns the "real" position of a static element or
    // relative element that does not have top/left explicitly set.
    // ==> Always set top and left for position relative elements in your stylesheets 
    // (to 0 if you do not need them) 
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if(this.options.mode == 'absolute') {
      // absolute movement, so we need to calc deltaX and deltaY
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
      top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
};

Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
  initialize: function(element, percent) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || {});
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle = {};
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if(fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if(this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if(/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if(!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if(this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = {};
    if(this.options.scaleX) d.width = Math.round(width) + 'px';
    if(this.options.scaleY) d.height = Math.round(height) + 'px';
    if(this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if(this.elementPositioning == 'absolute') {
        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if(this.options.scaleY) d.top = -topd + 'px';
        if(this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = {};
    if (!this.options.keepBackgroundImage) {
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage: 'none'});
    }
    if(!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if(!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    this.start(arguments[1] || {});
  },
  setup: function() {
    Position.prepare();
    var offsets = Position.cumulativeOffset(this.element);
    if(this.options.offset) offsets[1] += this.options.offset;
    var max = window.innerHeight ? 
      window.height - window.innerHeight :
      document.body.scrollHeight - 
        (document.documentElement.clientHeight ? 
          document.documentElement.clientHeight : document.body.clientHeight);
    this.scrollStart = Position.deltaY;
    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
  },
  update: function(position) {
    Position.prepare();
    window.scrollTo(Position.deltaX, 
      this.scrollStart + (position*this.delta));
  }
});

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
  from: element.getOpacity() || 1.0,
  to:   0.0,
  afterFinishInternal: function(effect) { 
    if(effect.options.to!=0) return;
    effect.element.hide().setStyle({opacity: oldOpacity}); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show(); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { 
    opacity: element.getInlineOpacity(), 
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element)
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().setStyle(oldStyle); }
     }, arguments[1] || {})
   );
}

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      } 
    }, arguments[1] || {})
  );
}

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || {}));
}

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { 
          effect.element.makePositioned().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
        }
      })
    }
  }, arguments[1] || {}));
}

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
        } 
      }, arguments[1] || {}));
}

Effect.Shake = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element, 
      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}) }}) }}) }}) }}) }});
}

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || {})
  );
}

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
      effect.element.down().undoPositioned();
    }
   }, arguments[1] || {})
  );
}

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, { 
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      effect.element.makeClipping(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping(); 
    }
  });
}

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) {
      effect.element.hide().makeClipping().makePositioned();
    },
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'}).show(); 
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
             }
           }, options)
      )
    }
  });
}

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
}

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || {};
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
}

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || {}));
};

Effect.Morph = Class.create();
Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: {}
    }, arguments[1] || {});
    if (typeof options.style == 'string') {
      if(options.style.indexOf(':') == -1) {
        var cssText = '', selector = '.' + options.style;
        $A(document.styleSheets).reverse().each(function(styleSheet) {
          if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
          else if (styleSheet.rules) cssRules = styleSheet.rules;
          $A(cssRules).reverse().each(function(rule) {
            if (selector == rule.selectorText) {
              cssText = rule.style.cssText;
              throw $break;
            }
          });
          if (cssText) throw $break;
        });
        this.style = cssText.parseStyle();
        options.afterFinishInternal = function(effect){
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            if(transform.style != 'opacity')
              effect.element.style[transform.style.camelize()] = '';
          });
        }
      } else this.style = options.style.parseStyle();
    } else this.style = $H(options.style)
    this.start(options);
  },
  setup: function(){
    function parseColor(color){
      if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0].underscore().dasherize(), value = pair[1], unit = null;

      if(value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if(property == 'opacity') {
        value = parseFloat(value);
        if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if(Element.CSS_LENGTH.test(value)) 
        var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;

      var originalValue = this.element.getStyle(property);
      return $H({ 
        style: property, 
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      });
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      )
    });
  },
  update: function(position) {
    var style = $H(), value = null;
    this.transforms.each(function(transform){
      value = transform.unit=='color' ?
        $R(0,2).inject('#',function(m,v,i){
          return m+(Math.round(transform.originalValue[i]+
            (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
        transform.originalValue + Math.round(
          ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
      style[transform.style] = value;
    });
    this.element.setStyle(style);
  }
});

Effect.Transform = Class.create();
Object.extend(Effect.Transform.prototype, {
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || {};
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      var data = $H(track).values().first();
      this.tracks.push($H({
        ids:     $H(track).keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var elements = [$(track.ids) || $$(track.ids)].flatten();
        return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');
  
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.prototype.parseStyle = function(){
  var element = Element.extend(document.createElement('div'));
  element.innerHTML = '<div style="' + this + '"></div>';
  var style = element.down().style, styleRules = $H();
  
  Element.CSS_PROPERTIES.each(function(property){
    if(style[property]) styleRules[property] = style[property]; 
  });
  if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) {
    styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
  }
  return styleRules;
};

Element.morph = function(element, style) {
  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
  return element;
};

['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
  function(f) { Element.Methods[f] = Element[f]; }
);

Element.Methods.visualEffect = function(element, effect, options) {
  s = effect.gsub(/_/, '-').camelize();
  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
  new Effect[effect_class](element, options);
  return $(element);
};

Element.addMethods();


Effect.MoveToElementAndRestore = function(element, toElement, afterFinishCallback) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, {});
  var dims = element.getDimensions();
  var moveX, moveY;
  var clipPosition = Position.cumulativeOffset($(toElement))
  moveX = clipPosition[0]-Position.cumulativeOffset(element)[0];
  moveY = clipPosition[1]-Position.cumulativeOffset(element)[1];
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
           if (typeof(afterFinishCallback) == "function") {
            afterFinishCallback();
           }
           }
       }, options)
  );
}

Effect.MoveToElement = function(element, toElement) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, {});
  var dims = element.getDimensions();
  var moveX, moveY;
  var clipPosition = Position.cumulativeOffset($(toElement))
  moveX = clipPosition[0]-Position.cumulativeOffset(element)[0];
  moveY = clipPosition[1]-Position.cumulativeOffset(element)[1];
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.parentNode.removeChild(effect.effects[0].element);            
           new Effect.Highlight(toElement);
           }
       }, options)
  );
}

Effect.MoveCloneToElement = function(element, toElement) {
  element = $(element);
  var oldElement = element;
  var newElement = document.createElement("div");
  newElement.innerHTML = $(element).innerHTML;
  newElement.id = "movingObject";
  element.parentNode.insertBefore(newElement, oldElement);
  element = $(newElement);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, {});
  var dims = element.getDimensions();
  var moveX, moveY;
  var clipPosition = Position.cumulativeOffset($(toElement))
  moveX = clipPosition[0]-Position.cumulativeOffset(element)[0];
  moveY = clipPosition[1]-Position.cumulativeOffset(element)[1];
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.parentNode.removeChild(effect.effects[0].element);            
           new Effect.Highlight(toElement);
           }
       }, options)
  );
}


String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); };
String.prototype.startsWith = function(s) { return this.indexOf(s)==0; }

function repositionFooter() {
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }

  if (!$('minHeightElement')) {
      var element = $('siteInfo');
      if (!element) return;
      if (element.style.position == "absolute") {
         element.style.position = "relative";
         element.style.top = "auto";
         element.style.width = "auto";
      }
      var offset = Position.cumulativeOffset(element);
      if (myHeight-30 > offset[1]) {
         element.style.position = "absolute";
         element.style.top = (myHeight-190)+"px";
         element.style.left = "0px";
         element.style.width="980px";
      }
  } else {
      var element = $('minHeightElement');
      element.style.height = "auto";
      var offset = Position.cumulativeOffset(element);
      var height = element.offsetHeight;
      if (myHeight-40 > offset[1]+height) {
         element.style.height = (height + (myHeight-height-offset[1]-40))+"px";
      }
  }
}

function floatSiteInfo() {
    var element = $('siteInfo');
    if (element) {
      element.style.position = "";
      element.style.top = "";
      element.style.width = "";
    }
    var element = $('minHeightElement');
    if (element) {
      element.style.height = "";
    }
}

function validCityElements(elementIds, optional) {
  var i=0;
  for (i=0;i<elementIds.length;i++) {
      if (!validCityElement(elementIds[i], optional)) {
        document.getElementById(elementIds[i]).focus();
      	return false;
      }
  }
  return true;
}

function validCityElement(elementId, optional, inputName, errorFunction) {
  cityEnteredValue = document.getElementById(elementId).value;
  cityId = document.getElementById(elementId+"_id").value;
  citySelectedValue = document.getElementById(elementId).getAttribute("oldValue");
  if (optional) {
  	if (cityEnteredValue.trim() == "" || cityEnteredValue == document.getElementById(elementId).getAttribute("info") ) {
	  	document.getElementById(elementId+"_id").value = "";
	  	document.getElementById(elementId).setAttribute("oldValue", "");
	  	return true;
  	}
  }
  var isValid = validCity(cityId, cityEnteredValue, citySelectedValue, inputName, errorFunction);
  if (!isValid)
    document.getElementById(elementId).focus();
  return isValid;
}

function validCity(cityId, cityEnteredValue, citySelectedValue, inputName, errorFunction) {
  if (!errorFunction) errorFunction = function(msg){alert(msg);}
  if (cityEnteredValue.toLowerCase().trim() == '') {
    if (inputName)
		errorFunction("Please enter "+inputName+".");
    else
		errorFunction("Please enter Cityname.");
  	return false;
  }
  if (cityId == '' || cityEnteredValue.toLowerCase().trim() != citySelectedValue.toLowerCase().trim()) {
    if (inputName)
		errorFunction("Invalid cityname for "+inputName+".  Please check and re-enter.");
    else
		errorFunction("Invalid cityname.  Please check and re-enter.");
  	return false;
  }
  return true;
}

function validCityElements(elementIds, optional) {
  var i=0;
  for (i=0;i<elementIds.length;i++) {
      if (!validCityElement(elementIds[i], optional)) {
        document.getElementById(elementIds[i]).focus();
      	return false;
      }
  }
  return true;
}

function validCityElement(elementId, optional, inputName, errorFunction) {
  cityEnteredValue = document.getElementById(elementId).value;
  cityId = document.getElementById(elementId+"_id").value;
  citySelectedValue = document.getElementById(elementId).getAttribute("oldValue");
  if (optional) {
  	if (cityEnteredValue.trim() == "" || cityEnteredValue == document.getElementById(elementId).getAttribute("info") ) {
	  	document.getElementById(elementId+"_id").value = "";
	  	document.getElementById(elementId).setAttribute("oldValue", "");
	  	return true;
  	}
  }
  var isValid = validCity(cityId, cityEnteredValue, citySelectedValue, inputName, errorFunction);
  if (!isValid)
    document.getElementById(elementId).focus();
  return isValid;
}

function validCity(cityId, cityEnteredValue, citySelectedValue, inputName, errorFunction) {
  if (!errorFunction) errorFunction = function(msg){alert(msg);}
  if (cityEnteredValue.toLowerCase().trim() == '') {
    if (inputName)
		errorFunction("Please enter "+inputName+".");
    else
		errorFunction("Please enter Cityname.");
  	return false;
  }
  if (cityId == '' || cityEnteredValue.toLowerCase().trim() != citySelectedValue.toLowerCase().trim()) {
    if (inputName)
		errorFunction("Invalid cityname for "+inputName+".  Please check and re-enter.");
    else
		errorFunction("Invalid cityname.  Please check and re-enter.");
  	return false;
  }
  return true;
}


function infoBoxFocus(elementId) {
    var element = $(elementId);
    if (element.value == element.getAttribute("info")) {
        element.value = "";
        element.className = element.className.replace("infoText", "");
    } else
        element.select();
}

function infoBoxBlur(elementId) {
    var element = $(elementId);
    if (element.value == "") {
        element.value = element.getAttribute("info");
        element.className = element.className.replace("infoText", "");
        element.className = element.className + " infoText";
    }
}

function clearInfoFields(frmName) {
    var frm = $(frmName);
    for (var i=0;i<frm.elements.length;i++) {
        clearInfoField(frm.elements[i]);
    }
}

function clearInfoField(element) {
    if (element.getAttribute("info") && element.value == element.getAttribute("info"))
        element.value = "";
}

function toggleDisplay() {
	for (var i=0;i<arguments.length;i++) {
		if (typeof(arguments[i]) == 'string')
	    	var element = document.getElementById(arguments[i]);
	    else
	    	var element = arguments[i];
		if (element) {
			if (element.style.display == "none")
				element.style.display = "";
			else
				element.style.display = "none";
		}
	}
	repositionFooter();
}

function toggleDisplaySlide(id) {
  var elem = $(id);
  if (elem) {
	if (elem.style.display == "none")
		slideDownEffect(id);
	else
		slideUpEffect(id);
  }
}

function lock_form(frm) {
  var f = document.getElementById(frm);
  if (!f) return;
  $A(f.elements).each(function(i,index){
    i.disabled = true;
  });
  Element.setOpacity(f, 0.5);
  f.old_onsubmit = f.onsubmit;
  f.onsubmit = function() { return false; }
  return false;
}

function unlock_form(frm) {
  var f = document.getElementById(frm);
  if (!f) return;
  $A(f.elements).each(function(i){
    i.disabled = false;
  });
  Element.setOpacity(f, 1.0);
  if (typeof f.old_onsubmit == 'function') f.onsubmit = f.old_onsubmit;
   return false;
}

function highlightElement(id, times) {
  if (!$(id) || $(id).getAttribute('inProgress') == "true") return;
  $(id).setAttribute("inProgress","true");
  new Effect.Highlight(id);
  var wait = 0;
  for(var i=0;i<times-1;i++) {
     wait += 1000;
     setTimeout(function(){new Effect.Highlight(id);}, wait);
  }
  setTimeout(function(){$(id).setAttribute("inProgress","");}, wait+1000);
}

function isValidEmail(email) {
	var Restr = /^[a-z]([\.\_]?[a-z\d]+)*\@[a-z\d]+([\.\_\-][a-z\d]+)+$/ig;
	return Restr.test(email);
}

function getCheckedValue(checkboxes) {
    for (var i=0;i<checkboxes.length;i++) {
        if (checkboxes[i].checked)
            return checkboxes[i].value;
    }
    return null;
}

function getCheckedValueByName(formName, boxName) {
    var frm = $(formName);
    for (var i=0;i<frm.elements.length;i++) {
        if ((frm.elements[i].type == 'radio' || frm.elements[i].type == 'checkbox') && frm.elements[i].name == boxName && frm.elements[i].checked)
            return frm.elements[i].value;
    }
    return null;
}

function click(element) {
  var element = $(element);
  if (!element) return;
  if (document.createEvent) {
    var evt = document.createEvent("MouseEvents");
    evt.initMouseEvent("click", true, true, window,
      0, 0, 0, 0, 0, false, false, false, false, 0, null);
    element.dispatchEvent(evt);
  } else {
    element.click();
  }
}


function check(checkboxes, value) {
    for (var i=0;i<checkboxes.length;i++) {
        if (checkboxes[i].value == value) {
            checkboxes[i].checked = true;
            break;
        }
    }
}

function checkAll(frm, check) {
    var frm = $(frm);
    for (var i=0;i<frm.elements.length;i++) {
        if ((frm.elements[i].type == 'radio' || frm.elements[i].type == 'checkbox'))
            frm.elements[i].checked = check;
    }
    return null;
}

function getCountChecked(frm, nameMatchRegex) {
  var numSelected = 0;
  for (var i = 0; i < frm.elements.length; i++) {
	var e = frm.elements[i];
	if ((e.disabled == false) && (e.type == 'checkbox') && e.name != "" && (!nameMatchRegex || nameMatchRegex.test(e.name))) {
		if (e.checked) numSelected++;
	}
  }
  return numSelected;
}

function getCheckedNames(frm, nameMatchRegex) {
  var checkedNames = [];
  for (var i = 0; i < frm.elements.length; i++) {
	var e = frm.elements[i];
	if ((e.disabled == false) && (e.type == 'checkbox') && e.name != "" && (!nameMatchRegex || nameMatchRegex.test(e.name))) {
		if (e.checked) checkedNames.push(e.name);
	}
  }
  return checkedNames;
}


function slideUpEffect(elementId, afterFinishFn) {
    floatSiteInfo();
    if (typeof(afterFinishFn) == "function")
        new Effect.SlideUp(elementId, {afterFinish:afterFinishFn});
    else
        new Effect.SlideUp(elementId);
}

function slideDownEffect(elementId, afterFinishFn) {
    floatSiteInfo();
    if (typeof(afterFinishFn) == "function")
        new Effect.SlideDown(elementId, {afterFinish:afterFinishFn});
    else
        new Effect.SlideDown(elementId);
}

function getSelectedOption(selectElement) {
    selectElement = $(selectElement);
    if (!selectElement) return null;
	return selectElement.options[selectElement.selectedIndex];
}

function getSelectedValue(selectElement) {
	var option = getSelectedOption(selectElement);
	if (option) return option.value;
	return null;
}



function xajaxSetCookie(sName, sValue, nDays) {
  var expires = "";
  if (nDays) {
    var d = new Date();
    d.setTime(d.getTime() + nDays * 24 * 60 * 60 * 1000);
    expires = '; expires=' + d.toGMTString();
  }
  document.cookie = sName + '=' + sValue + expires + '; path=/';
}

function adjustElementHeights(sections) {
  var maxHeight = 0;
  for (var i=0;i<sections.length;i++) {
    if (sections[i]) {
        sections[i].style.height = "auto"
        if (sections[i].scrollHeight > maxHeight)
            maxHeight = sections[i].scrollHeight;
    }
  }
  if (maxHeight == 0) return;
  for (var i=0;i<sections.length;i++) {
    if (sections[i]) {
       sections[i].style.height = (maxHeight+10)+"px";
    }
  }
}

function showProgress(element, height) {
  element = $(element);
  if (!element) return;
  var img = document.createElement('img');
  img.src = "/i/tj/images/progress_black.gif";
  while(element.firstChild)
	 element.removeChild(element.firstChild);
  element.appendChild(img);
  if (height) {
	img.style.marginBottom = height+"px";
  }
  element.show()
  repositionFooter();
}


function createCalendar(name, mindate) {
  if (typeof(YAHOO.datepicker[name+"Picker"]) != 'undefined') {
    YAHOO.datepicker[name+'Picker'].selectEvent.unsubscribeAll();
    delete YAHOO.datepicker[name+'Picker'];
  }
  var options = {pages:2, close:false};
  if (mindate != '') options.mindate = mindate;
  YAHOO.datepicker[name+"Picker"] = new YAHOO.widget.CalendarGroup(name,name+'Container', options);
  YAHOO.datepicker[name+'Picker'].render();
  YAHOO.datepicker[name+'Picker'].selectEvent.subscribe(handleCalendarSelect, YAHOO.datepicker[name+'Picker'], true);
  if (mindate == '' && !$(name).getAttribute('allowPast')) {
	YAHOO.datepicker[name+'Picker'].cfg.setProperty('mindate', new Date());;
  }
}
function getCalendarDateStr(cal) {
  var selectedDates = cal.getSelectedDates();
  var str = '';
  if (selectedDates.length > 0) {
    var firstDate = selectedDates[0];
    var year = firstDate.getFullYear(), month = firstDate.getMonth()+1, day = firstDate.getDate();
    str = (month<10?"0":"")+month + "/"+(day<10?"0":"")+ day + "/" + year;
  }
  return str;
}

function getLeftTop(o) {
	var fixBrowserQuirks = true;

	var left = 0;
	var top = 0;
	var parentNode = null;
	var offsetParent = null;

	offsetParent = o.offsetParent;
	var originalObject = o;
	var el = o; // "el" will be nodes as we walk up, "o" will be saved for
				// offsetParent references
	while (el.parentNode != null) {
		el = el.parentNode;
		if (el.offsetParent == null) {
		} else {
			var considerScroll = true;
			if (fixBrowserQuirks && window.opera) {
				if (el == originalObject.parentNode || el.nodeName == "TR") {
					considerScroll = false;
				}
			}
			if (considerScroll) {
				if (el.scrollTop && el.scrollTop > 0) {
					top -= el.scrollTop;
				}
				if (el.scrollLeft && el.scrollLeft > 0) {
					left -= el.scrollLeft;
				}
			}
		}
		// If this node is also the offsetParent, add on the offsets and reset
		// to the new offsetParent
		if (el == offsetParent) {
			left += o.offsetLeft;
			if (el.clientLeft && el.nodeName != "TABLE") {
				left += el.clientLeft;
			}
			top += o.offsetTop;
			if (el.clientTop && el.nodeName != "TABLE") {
				top += el.clientTop;
			}
			o = el;
			if (o.offsetParent == null) {
				if (o.offsetLeft) {
					left += o.offsetLeft;
				}
				if (o.offsetTop) {
					top += o.offsetTop;
				}
			}
			offsetParent = o.offsetParent;
		}
	}
	return {left: left, top: top};
}

function showCalendar(cal, text) {
  infoBoxFocus(text.id);
  var div = $(cal.id+'Container');
  if (div.parentNode != document.body) {
	  div.parentNode.removeChild(div);
	  document.body.appendChild(div);
  }
  var pos = getLeftTop($(text));
  div.style.left = (pos.left)+"px";
  div.style.top = (pos.top+22)+"px";
  div.style.width = "280px";
  div.style.fontSize="8pt";
  if (text.value.trim() != '' && getCalendarDateStr(cal) != text.value) {
      cal.select(text.value);
      var selectedDates = cal.getSelectedDates();
      if (selectedDates.length > 0) {
        var firstDate = selectedDates[0];
        cal.cfg.setProperty("pagedate", (firstDate.getMonth()+1) + "/" + firstDate.getFullYear());
      }
  }
  cal.render();
  cal.show();
}

function hideCalendar(cal, text) {
//  validateDate(text);
  setTimeout(function(){cal.hide()},200);
}

function handleCalendarSelect(type,args,obj) {
  var dates = args[0];
  var date = dates[0];
  var year = date[0], month = date[1], day = date[2];
  if (isNaN(year)) return;
  var txtDate = document.getElementById(obj.id);
  txtDate.value = (month<10?"0":"")+month + "/"+(day<10?"0":"")+ day + "/" + year;
}

function handleDepartCalendarSelect(type, args, obj) {
  var dates = args[0];
  var date = dates[0];
  var year = date[0], month = date[1], day = date[2];
  if (isNaN(year)) return;
  if (obj.cfg.getProperty('mindate') != month + "/" + day + "/" + year) {
    obj.cfg.setProperty("mindate", month + "/" + day + "/" + year);
    obj.render();
  }
  var retDateTxt = document.getElementById(obj.id);
  var depDate = Date.parse(month + "/"+day + "/" + year);
  if (retDateTxt.value.trim() == '' || depDate > Date.parse(retDateTxt.value)) {
      retDateTxt.value = (month<10?"0":"")+month + "/"+(day<10?"0":"")+ day + "/" + year;
      obj.select(retDateTxt.value);
  }
}

function linkDepartReturnDates(departCal, returnCal) {
  departCal.selectEvent.subscribe(handleDepartCalendarSelect, returnCal, true);
  if ($(departCal.id).value.trim() != '')
     departCal.select($(departCal.id).value);
}

/** Set only the year, month, date parts (keep existing time) */
Date.prototype.setDateOnly = function(date) {
  var tmp = new Date(date);
  this.setDate(1);
  this.setFullYear(tmp.getFullYear());
  this.setMonth(tmp.getMonth());
  this.setDate(tmp.getDate());
};


function urchinTracker(url){
  if (typeof(pageTracker) != 'undefined' && pageTracker)
     pageTracker._trackPageview(url);
}


// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini

// http://dean.edwards.name/weblog/2005/10/add-event/

function addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		// assign each event handler a unique ID
		if (!handler.$$guid) handler.$$guid = addEvent.guid++;
		// create a hash table of event types for the element
		if (!element.events) element.events = {};
		// create a hash table of event handlers for each element/event pair
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			// store the existing event handler (if there is one)
			if (element["on" + type]) {
				handlers[0] = element["on" + type];
			}
		}
		// store the event handler in the hash table
		handlers[handler.$$guid] = handler;
		// assign a global event handler to do all the work
		element["on" + type] = handleEvent;
	}
};
// a counter used to create unique IDs
addEvent.guid = 1;

function removeEvent(element, type, handler) {
	if (element.removeEventListener) {
		element.removeEventListener(type, handler, false);
	} else {
		// delete the event handler from the hash table
		if (element.events && element.events[type]) {
			delete element.events[type][handler.$$guid];
		}
	}
};

function handleEvent(event) {
	var returnValue = true;
	// grab the event object (IE uses a global event object)
	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
	// get a reference to the hash table of event handlers
	var handlers = this.events[event.type];
	// execute each event handler
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};

function fixEvent(event) {
	// add W3C standard event methods
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
	this.cancelBubble = true;
};
/** ends **/

/**
 * Creates an Element for insertion into the DOM tree.
 * From http://simon.incutio.com/archive/2003/06/15/javascriptWithXML
 *
 * @param element the element type to be created.
 *				e.g. ul (no angle brackets)
 **/
function createElement(element) {
	if (typeof document.createElementNS != 'undefined') {
		return document.createElementNS('http://www.w3.org/1999/xhtml', element);
	}
	if (typeof document.createElement != 'undefined') {
		return document.createElement(element);
	}
	return false;
}

/**
 * "targ" is the element which caused this function to be called
 * from http://www.quirksmode.org/js/events_properties.html
 **/
function getEventTarget(e) {
	var targ;
	if (!e) {
		e = window.event;
	}
	if (e.target) {
		targ = e.target;
	} else if (e.srcElement) {
		targ = e.srcElement;
	}
	if (targ.nodeType == 3) { // defeat Safari bug
		targ = targ.parentNode;
	}

	return targ;
}


function forceLoadPage(url, destinationElementId, showProgress) {
  $(destinationElementId).setAttribute("loaded", false);
  loadPage(url, destinationElementId, showProgress);
}

function loadPage(url, destinationElementId, showProgressFlg) {
	var loaded = $(destinationElementId).getAttribute("loaded");
	if (loaded == 'true') return;
	urchinTracker(url);
	if (showProgressFlg) {
	  showProgress(destinationElementId);
	  repositionFooter();
	}
	if (url.indexOf("?") <= 0) url += "?"; else url += "&";
	url += "async=true";
    new Ajax.Updater(
    	destinationElementId,
    	url,
    	{
    		onComplete:function(){
    			$(destinationElementId).setAttribute("loaded", 'true');
    			repositionFooter();
    		},
		    asynchronous:true,
		    evalScripts:true
		}
	);
}

function switchTab(newTab) {
  var current = document.getElementById(newTab);
  while (current && current.nodeName.toLowerCase() != 'ul') {
    current = current.parentNode;
  }
  if (current) {
    var links = current.getElementsByTagName("a");
    for (var i=0;i<links.length;i++) {
      var id = links[i].getAttribute('id');
      var content = document.getElementById(id+"Contents");
      if (id == newTab) {
          if (links[i].className.indexOf("active") >= 0) return true;
          links[i].className = links[i].className.replace(/[\s]*active/gim, '')+' active';
          if (content) content.style.display = "";
      } else {
          links[i].className = links[i].className.replace(/[\s]*active/gim, '');
          if (content) content.style.display = "none";
      }
    }
  }
  repositionFooter();
  return false;
}

function dynamicSwitchTab(newTab, force) {
  switchTab(newTab);
  if (typeof(force) == 'undefined' || !force)
  	loadPage($(newTab).href, newTab+"Contents", true);
  else
    forceLoadPage($(newTab).href, newTab+"Contents", true);
  $(newTab).onclick = function(){return switchTab(newTab);}
  return false;
}

function HelpTopic(helpID) {
	for (i=1;true;i++) {
	    if (!$("help"+i)) break;
		$("help"+i).style.display = "none";
		$("hlink"+i).className = "helpNavLink";
	}

    if ($("hlink"+helpID)) {
    	$("hlink"+helpID).className = "helpNavLinkActive";
    	$("help"+helpID).style.display = "";
	} else {
	  alert("Invalid help topic "+helpID);
	}
	repositionFooter();
}

function openPlainWindow(url, width, height){
    var left = (screen.width - width) / 2;
    var top = (screen.height - height) / 2;
	win=window.open(url,'','toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=no,width='+width+',height='+height+',left='+left+',top='+top);
	win.focus();

}

function openResizableWindow(url, width, height){
    var left = (screen.width - width) / 2;
    var top = (screen.height - height) / 2;
	win=window.open(url,'','toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width='+width+',height='+height+',left='+left+',top='+top);
	win.focus();

}

function plural(text, count) {
	count = Math.abs(count);
	if (count != 1) return text+"s";
	return text;
}
function maintainDynamicTime() {
	var italics = document.getElementsByTagName("i");
	for (var i=0;i<italics.length;i++) {
		try {
			if (italics[i].className == 'dynamicTime') {
				var time = eval(italics[i].getAttribute('time'));
				var now = eval(italics[i].getAttribute('now'));
				now.setMinutes(now.getMinutes()+1);
				italics[i].setAttribute('now', 'new Date('+now.dateFormat('Y, m-1, d, H, i, s')+');');
				italics[i].innerHTML = timeForToday(now, time, "M d", "on ", true);
			}
		} catch(e){}
	}
	setTimeout(maintainDynamicTime, 60*1000);
}
function timeForToday(now, time, format, prefix, minimizeDays) {
	var diff = (now.getTime()-time.getTime())/1000;
	if (diff/(60*60)<24) {
		if (diff/(60*60) < 1) {
			var mins = Math.ceil((diff%3600)/60);
			return mins+" "+plural("minute", mins)+" ago";
		}
		var hrs = Math.round(diff/(60*60));
		return hrs+" "+plural("hour", hrs)+" ago";
	}
	if (minimizeDays) {
		days = floor(diff/(60*60*24));
		if (days < 5) {
			return days+" "+plural("day", days)+" ago";
		}
	}
	text = time.dateFormat(format, dateTime);
	return prefix+text;
}

if (typeof(Effect) != 'undefined') {
	Effect.SlideLeft = function(element) {
	  element = $(element);
	  var oldStyle = {
	    top: element.style.top,
	    left: element.style.left,
	    width: element.style.width,
	    height: element.style.height };
	  element.makeClipping();
	  return new Effect.Scale(element, 0, Object.extend({
	      scaleContent: false,
	      scaleY: false,
	      afterFinishInternal: function(effect) {
	        effect.element.hide().undoClipping().setStyle(oldStyle);
	      }}, arguments[1]||{}));
	};
	Effect.SlideRight = function(element) {
	  element = $(element);
	  var elementDimensions = element.getDimensions();
	  return new Effect.Scale(element, 100, Object.extend({
	    scaleContent: false,
	    scaleY: false,
	    scaleFrom: 0,
	    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
	    restoreAfterFinish: true,
	    afterSetup: function(effect) {
	      effect.element.makeClipping().setStyle({width: '0px'}).show();
	    },
	    afterFinishInternal: function(effect) {
	      effect.element.undoClipping();
	    }
	  }, arguments[1] || {}));
	};
}

function showTime(id, prefix) {
  clearTimeout(window['st_'+id]);
  var element = $(id);
  if (!element) return;
  var curTime = parseInt(element.getAttribute('time'));
  if (element.innerHTML != '') {
	  curTime += 1;
	  element.setAttribute('time', curTime);
  }
  var min = Math.floor(curTime/60);
  var sec = curTime%60;
  element.innerHTML = prefix+(min<10?'0':'')+min+":"+(sec<10?'0':'')+sec;
  window['st_'+id] = setTimeout(function(){showTime(id, prefix)}, 1000);
}

function disable(elementId) {
   if ($(elementId))
    new Effect.Puff(elementId);
}


Event.observe(window, "load", function(){setTimeout(maintainDynamicTime, 60*1000)});


var ie6=false;
agt=navigator.userAgent.toLowerCase();
var is_ie6 = (agt.indexOf('msie')!= -1);
ie6 = (is_ie6 && (navigator.appVersion.indexOf("MSIE 6.0") != -1));

function openWindow(url, name, params) {
	var wnd = window.open(url, name, params);
	wnd.focus();
}


function enableDisableButton(textBoxId, buttonId) {
    if ($(textBoxId).value.trim() != "" && $(textBoxId).getAttribute("info") != $(textBoxId).value) {
       $(buttonId).style.display = "";
    } else {
       $(buttonId).style.display = "none";    
    }
}

function validTagElement(elementId, optional, inputName) {
  tagEnteredValue = document.getElementById(elementId).value;
  tagId = document.getElementById(elementId+"_id").value;
  tagSelectedValue = document.getElementById(elementId).getAttribute("oldValue");
  if (optional) {
  	if (tagEnteredValue.trim() == "" || tagEnteredValue == document.getElementById(elementId).getAttribute("info") ) {
	  	document.getElementById(elementId+"_id").value = "";
	  	document.getElementById(elementId).setAttribute("oldValue", "");
	  	return true;
  	}
  }
  var isValid = validTag(elementId, tagId, tagEnteredValue, tagSelectedValue, inputName);
  if (!isValid)
    document.getElementById(elementId).focus();
  return isValid;
}

function validTag(elementId, tagId, tagEnteredValue, tagSelectedValue, inputName) {
  if (tagId == '' || tagEnteredValue.toLowerCase().trim() != tagSelectedValue.toLowerCase().trim()) {
    var addMsg = "\nIf you don't find the label you need, go ahead and add one.";
    if ($(elementId).getAttribute('noAdd') == "1")
      addMsg = "";
    if (inputName)
		alert("Please select a valid label for "+inputName+"."+addMsg);
    else
		alert("Please select a valid label. "+addMsg);
	
  	return false;
  }
  return true;
}

function validVenueElement(elementId, optional, inputName) {
  venueEnteredValue = document.getElementById(elementId).value;
  venueId = document.getElementById(elementId+"_id").value;
  venueSelectedValue = document.getElementById(elementId).getAttribute("oldValue");
  if (optional) {
  	if (venueEnteredValue.trim() == "" || venueEnteredValue == document.getElementById(elementId).getAttribute("info") ) {
	  	document.getElementById(elementId+"_id").value = "";
	  	document.getElementById(elementId).setAttribute("oldValue", "");
	  	return true;
  	}
  }
  var isValid = validVenue(venueId, venueEnteredValue, venueSelectedValue, inputName);
  if (!isValid)
    document.getElementById(elementId).focus();
  return isValid;
}

function validVenue(venueId, venueEnteredValue, venueSelectedValue, inputName) {
  if (venueId == '' || venueEnteredValue.toLowerCase().trim() != venueSelectedValue.toLowerCase().trim()) {
    if (inputName)
		alert("Please select a valid venue for "+inputName+". \nIf you don't find the venue you need, go ahead and add one.");
    else
		alert("Please select a valid venue. \nIf you don't find the venue you need, go ahead and add one.");
	
  	return false;
  }
  return true;
}

function addEntry(entryId, entryName, type) {
  var inputName = 'new_' + type + '[' + entryId + ']';
  // don't double add
  if (entryForm[inputName] && entryForm[inputName].value == entryId) {
    if(type=='member'){
    alert('\''+entryName+'\' is already listed as a probable traveler');
	}
    return;
  }
  var entryIDTemp=entryId+'_'+ type;
  if(document.getElementById(entryIDTemp) != null) {
	 if(document.getElementById(entryIDTemp).innerHTML == entryIDTemp) {
		 if(type=='member'){
    		alert('\''+entryName+'\' is already listed as a probable traveler');
		}
    	return;
	 }
  }

  pageDataModified = true;

  var entryTable = document.getElementById(type + 'List');
  var rowNum = entryTable.getElementsByTagName('tr').length;
  var entryLinkName = type + 'List_' + rowNum;

  showCheckMark(entryTable, type);
    // add to table visually
  var tbody = document.createElement('tbody');
  var tr = document.createElement('tr');
  var td = document.createElement('td');
  var removeTd = document.createElement('td');
  if (type == 'member') {
    var image = document.createElement('img');
    image.src = '/user/showpicture/' + entryId+'.jpg';
	image.height = 30;
	image.width = 30;
	image.align = 'top';
    td.appendChild(image);
	entryName = '<a target="_blank" class="profileLink" href="/site/search?submit=true&withinFriends=true&userid='+ entryId +'" >'+ entryName+'</a>';
  }else if (type == 'destination'){
    entryName = '<a target="_blank" class="profileLink" href="/site/search?submit=true&airport_code_id='+entryId+'&airport_code='+entryName+'">'+entryName+'</a>';
  }

  td.innerHTML = entryName;

  removeTd.innerHTML = '<span id="' + entryIDTemp + '" style="display:none" >'+ entryIDTemp +'</span><a id="' + entryLinkName +'" href="#" onclick="removeEntry(this, \'' + entryId + '\',\'' + type + '\');return false;">&nbsp;&nbsp;remove</a>';
  tr.appendChild(td);
  tr.appendChild(removeTd);
  tbody.appendChild(tr);
  entryTable.appendChild(tbody);

  // add to FORM for submit
  if (entryForm[inputName]) {
    entryForm[inputName].value = entryId;
  } else {
    var input = document.createElement('input');
    input.type = 'hidden';
    input.name = inputName;
    input.value = entryId;
    entryForm.appendChild(input);
  }

  if(type == 'destination' && document.getElementById("whereNext") != null )
  	document.getElementById("whereNext").style.display = "";
  if(type == 'member' && document.getElementById("whoNext") != null )
  	document.getElementById("whoNext").style.display = "";
  if(type == 'date' && document.getElementById("whenNext") != null )
  	document.getElementById("whenNext").style.display = "";

}

function removeEntry(entry, id, type) {
  var inputs = entryForm.getElementsByTagName('input');
  var element;
  for (var i = 0; i < inputs.length; i++) {
    if (inputs[i].name == 'new_' + type + '[' + id + ']') {
      element = inputs[i];
    }
  }

  if (element) {
    if (element.value != undefined && element.value == id) {
      element.value = null;
    } else if (element.length > 0) {
      for(var i = 0; i < element.length; i++) {
        if (element[i].value == id) {
          element[i].value = null;
        }
      }
    }

    var td = entry.parentNode;
    var tr = td.parentNode;
    var table = tr.parentNode;
    table.removeChild(tr);
	    if (table.getElementsByTagName('tr').length == 1) {
	      // change the checkbox to a question mark
	      var image = document.getElementById(type + 'RequiredImage');
	      image.src = '/i/' + type + '_question.gif';
	    }
  }
}

function showCheckMark(table, type) {
  // change the question mark to a checkbox
  var image = document.getElementById(type + 'RequiredImage');
  if (image)
	  image.src = '/i/images/16x16_checked.gif';
}

function hitEnterPressButton(button, event) {
  var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
  if (keyCode == 13) {
    button.click();
    return false;
  }
  return true;
}

function doFormSubmit(childElement)
{
	var parent = childElement.parentNode;
	while(parent != null)
	{
		if (parent.method)
		{
			parent.submit();
			return false;
		}
		else
			parent = parent.parentNode;
	}
	return false;
}

function showHelp(site, topic) {
  var helpURL = "/site/page?p="+site+"/help&noheader=true&t="+topic;
  Dialog.window({url: helpURL, options: {method: "get"}}, {windowParameters: {width:900, height:500, zIndex:2000}}); 
  return false;
}

function descCount() {
	var maxlen = 99;
	var objVal = document.getElementById("activity_description").value;
	var wordCount = objVal.split(" ");
	if (wordCount.length > maxlen + 1) {
		document.getElementById("activity_description").maxLength = objVal.length;
	} else {
		document.getElementById("activity_description").maxLength = 1000;
	}
}
function textLimit(field, maxlen) {
	if (field.value.length > maxlen)
		field.value = field.value.substring(0, maxlen);
}
function descLimit(field, maxlen) {
	if (field.value.length > maxlen) {
		field.value = field.value.substring(0, maxlen);
		alert("Description exceeds "+maxlen+" characters.\nYou can enter longer descriptions in the 'daily notes' section directly below, or in 'Photos & Details' page.  Thanks.");
		return false;
	}
}
//======================== Cookies =======================================

function setEmailFocus() {
	var f = null;
	if (document.getElementById) {
		f = document.getElementById("loginfrm");
	} else if (window.loginfrm) {
		f = window.loginfrm;
	}
	if (f != null) {
		if (f.signinemail.value == null || f.signinemail.value == "") {
			f.signinemail.focus();
		} else {
			f.signinpassword.focus();
		}
	}
}

function validateSubjectBody(frmId) {
	var frm = document.getElementById(frmId);
	if (!frm) return false;
	if (frm.subject && frm.subject.value == "") {
		alert("Please enter subject.");
		frm.subject.focus();
		return false;
	}
	if (frm.body.value == "") {
		alert("Please enter message.");
		frm.body.focus();
		return false;
	}
	return true;
}

function showEnlargedImage(url, title) {
	var height = WindowUtilities.getWindowScroll().height;
	dlg = Dialog.window("<img src='"+url+"' height="+(height-100)+"/>",
             {windowParameters: {width:700, height:(height-50), zIndex:2000}});
	return false;
}

function deleteFriend(friendId) {
	if(confirm("Do you want to delete this friend?"))
		window.location.href="/user/deletefriend/"+friendId;
    return false;
}

function deleteGroup(groupId, member) {
    msg = "Do you want to go out of this group?";
    if (member == 0) {
        msg = "Do you want to delete this group?";
	}     	
	if(confirm(msg))
    	window.location.href="/group/delete/"+groupId+"?removeMembership="+member;
    return false;
}

function deleteTrip(tripId, isOrganizer, fromMyTravel, title)
{
   if(isOrganizer == 1) {
     if(confirm("Are you sure you want to delete this "+title+"?"))
	    window.location.href="/trip/delete/"+tripId+"?isFromMyTravel="+fromMyTravel;
   } else {
	 if(confirm("Are you sure you want to be removed from this "+title+"?"))
	    window.location.href="/trip/delete/"+tripId+"?isFromMyTravel="+fromMyTravel;
   }
}

function validateDate(fld) {
	if (fld.value == '') return;
    var RegExPattern = /^(?=\d)(?:(?:(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})|(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2}))($|\ (?=\d)))?(((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))|([01]\d|2[0-3])(:[0-5]\d){1,2})?$/;
    var errorMessage = 'Please enter a valid date.\nFormat mm/dd/yyyy';
    if (!fld.value.match(RegExPattern)) {
        alert(errorMessage);
        setTimeout("document.getElementById('"+fld.id+"').focus();", 50);
    }
}

function showComments(talkId, url) {
    document.getElementById("comments"+talkId+"Row1").style.display='block';
    if (document.getElementById("comments"+talkId+"Row2"))
	    document.getElementById("comments"+talkId+"Row2").style.display='block';
    loadPage(url, "comments"+talkId);
}

//Email Validation
function ValidateEmail(theForm){
	if(theForm.friend_id.value != '')  return true;
	
	if(theForm.email.value != "") {
		if (!isValidEmail(theForm.email.value)){
			theForm.email.focus();
			alert("You must enter valid email address");
			return false;
		}
		return validCityElement('origin_city', true, 'Home town');
	}
	if(theForm.emails.value != '' && theForm.emails.value != theForm.emails.getAttribute("info")) return true;
	alert("You must select friend or enter email to add user to trip");
	return false;
}


function animateNotice(){
  document.getElementById("notice").style.display = "block";
  if (document.getElementById("notice").scrollWidth > 630) {
    document.getElementById("notice").style.width="630px"; 
  } 
  if (document.getElementById("notice").scrollHeight > 70) {
    document.getElementById("notice").style.height="65px"; 
    document.getElementById("notice").style.overflow = 'scroll';
  } 
  highlightElement('notice', 3); 
}


function displayAddTag(id, type, anchor) {
    toggleDisplay(anchor);
    toggleDisplay('add_'+type+'_'+id);
    if ($('activityRow'+id))
        $('activityRow'+id).style.zIndex = 2001;
    return false;
}

function assignTag(entity_id, type) {
    if (validTagElement(type+'_input_'+entity_id, false)) {
        var tag_id = document.getElementById(type+'_input_'+entity_id+"_id").value;
        xajax_assignTag(entity_id, tag_id, type);
    }
}

function detachTag(entity_id, tag_id, type) {
    if (confirm("You want to remove this label. Are you sure?"))
        xajax_detachTag(entity_id, tag_id, type);
}

function assignInnerHTML(elementId, html) {
    var dummy = document.createElement("div");
    dummy.insertAdjacentHTML("AfterBegin", html);
    $(elementId).innerHTML = "";
    $(elementId).appendChild(dummy.firstChild);
}

function isDigit(c) {
	return ((c=='0')||(c=='1')||(c=='2')||(c=='3')||(c=='4')||(c=='5')||(c=='6')||(c=='7')||(c=='8')||(c=='9'))
}

function validNumber(elementId, fieldName) {
    if (!isNumeric($(elementId).value)) {
        alert("Please enter valid number for "+fieldName);
        $(elementId).focus();
        return false;
    }
    return true;
}

function isNumeric(n) {
	num = parseInt(n,10);
	return !isNaN(num);
}

function padZero(n) {
	v="";
	if (n<10){ 
		return ('0'+n);
	} else {
		return n;
	}
}

function validateTimePicker(ctl) {
    var mode="";
	t=ctl.value.toLowerCase();
	t=t.replace(" ","");
	t=t.replace(".",":");
	t=t.replace("-","");

	if ((isNumeric(t))&&(t.length==4))
		t=t.charAt(0)+t.charAt(1)+":"+t.charAt(2)+t.charAt(3);

	var t=new String(t);
	tl=t.length;

	if (tl==1 ) {
		if (isDigit(t)) {
			ctl.value=t+":00 am";
		} else {
		    ctl.value = "";
			return false;
		}
	}
	else if (tl==2) {
		if (isNumeric(t)) {
			if (parseInt(t,10)<13){
				if (t.charAt(1)!=":") {
					ctl.value= t + ':00 am';
				} else {
					ctl.value= t + '00 am';
				}
			} else if (parseInt(t,10)==24) {
				ctl.value= "0:00 am";
			} else if (parseInt(t,10)<24) {
				if (t.charAt(1)!=":") {
					ctl.value= (t-12) + ':00 pm';
				} else {
					ctl.value= (t-12) + '00 pm';
				}
			} else if (parseInt(t,10)<=60) {
				ctl.value= '0:'+padZero(t)+' am';
			} else {
				ctl.value= '1:'+padZero(t%60)+' am';
				}
			} else {
				if ((t.charAt(0)==":")&&(isDigit(t.charAt(1)))) {
				ctl.value = "0:" + padZero(parseInt(t.charAt(1),10)) + " am";
			} else {
    		    ctl.value = "";
    			return false;
			}
		}
	} else if (tl>=3) {
		var arr = t.split(":");
		if (t.indexOf(":") > 0) {
			hr=parseInt(arr[0],10);
			mn=parseInt(arr[1],10);

			if (t.indexOf("pm")>0) {
				mode="pm";
			} else {
				mode="am";
			}

			if (isNaN(hr)) {
				hr=0;
			} else {
				if (hr>24) {
        		    ctl.value = "";
        			return false;
				} else if (hr==24) {
					mode="am";
					hr=0;
				} else if (hr>12) {
					mode="pm";
					hr-=12;
				}
			}
		
			if (isNaN(mn)) {
				mn=0;
			} else {
				if (mn>60) {
					mn=mn%60;
					hr+=1;
				}
			}
		} else {

			hr=parseInt(arr[0],10);

			if (isNaN(hr)) {
    		    ctl.value = "";
    			return false;
			} else {
				if (hr>24) {
        		    ctl.value = "";
        			return false;
				} else if (hr==24) {
					mode="am";
					hr=0;
				} else if (hr>12) {
					mode="pm";
					hr-=12;
				}
			}

			mn = 0;
		}
		
		if (hr==24) {
			hr=0;
			mode="am";
		}
		ctl.value=hr+":"+padZero(mn)+" "+mode;
	}
}

function validUrl(elementId) {
    var element = $(elementId);
    if (element) {
        if (element.value.trim() == "") {
            element.focus();
            return false;
        }
    }
    return true;
}

function verifyLogin() {
  xajax_verifyLogin(xajax.getFormValues('loginfrm'));
}

function verifyRegister() {
    if (validCityElement('origin_city', false, 'City I Live In')) {
      termsCheckbox = document.getElementById("termsCheck");
    	if(termsCheckbox != null) {
    		if(termsCheckbox.checked == false) {
    			alert("Sorry! You need to accept terms of service before you sign up");
    			return false;
    		}
    	}
        xajax_verifyRegister(xajax.getFormValues('registerfrm'));
    }
}

function getFlightBookingUrl(bookingSite, originAirportCode, destinationAirportCode, departureDateFormatted, returnDateFormatted, priceLineDepartureDate, priceLineReturnDate, orbDeparture, orbDepartureDate, orbArrival, orbArrivalDate) {    
    var flightBookingUrl = new Array();
    flightBookingUrl['expedia'] = "'http://service.bfast.com/bfast/click?bfmid=26917872&siteid=41635796&bfpage=deeplink&GOTO=EXPFLTWIZ&FrAirport=' +originAirportCode + '&ToAirport=' +destinationAirportCode + '&FromDate=' + departureDateFormatted + '&ToDate=' + returnDateFormatted + '&NumAdult=1&NumChild=0&FromTime=Morning&ToTime=Morning'";
    flightBookingUrl['hotwire'] = "'http://click.linksynergy.com/fs-bin/statform?id=8KQyBpa6sz8&offerid=93322&bnid=978&subid=0&origCity=' +originAirportCode + '&destinationCity=' +destinationAirportCode + '&startDate=' + departureDateFormatted + '&endDate=' + returnDateFormatted + '&noOfTickets=2&inputId=index'";
    flightBookingUrl['travelocity'] = "'http://travel.travelocity.com/flights/InitialSearch.do?Service=CJUS&dateLeavingTime=Anytime&dateReturningTime=Anytime&flightType=roundtrip&leavingFrom=' +originAirportCode + '&goingTo=' +destinationAirportCode + '&leavingDate=' + departureDateFormatted + '&returningDate=' + returnDateFormatted + '&aid=10398279&pid=2124264&url=http://travel.travelocity.com/flights/InitialSearch.do'";
    flightBookingUrl['priceline'] = "'http://www.kqzyfj.com/interactive?name=pclnbanner_air&id=pclnbanner_air&ProductID=1&DepCity=' +originAirportCode + '&ArrCity=' +destinationAirportCode + '&DepartureDate=' + priceLineDepartureDate + '&ReturnDate=' + priceLineReturnDate + '&NumTickets=1&aid=10408266&pid=2124264&url=http://www.priceline.com/qp.asp'";
    flightBookingUrl['orbitz'] = "'http://www.orbitz.com/App/ValidateFlightSearch?id=8KQyBpa6sz8&offerid=66478&type=4&subid=&slice1:departCity=' +originAirportCode + '&slice1:arriveCity=' +destinationAirportCode + '&Slice1:month=' + orbDeparture + '&Slice1:day=' + orbDepartureDate + '&Slice2:month=' + orbArrival + '&Slice2:day=' + orbArrivalDate + '&adult=1'";
    flightBookingUrl['kayak'] = "'http://www.kayak.com/s/search/air?id=8KQyBpa6sz8&offerid=100094&type=4&subid=0&ai=8KQyBpa6sz8&l1=' +originAirportCode + '&l2=' +destinationAirportCode + '&d1=' + departureDateFormatted + '&d2=' + returnDateFormatted + '&ft=rt&pa=1&cb=Economy'";

    urlLink = eval(flightBookingUrl[bookingSite]);
    
    javascript : urchinTracker(urlLink);
    return urlLink;
}

function getHotelBookingUrl(bookingSite, destination, startDate, endDate, startMonthYear, startDay, endMonthYear, endDay) {
    var hotelBookingUrl = new Array();
    hotelBookingUrl['expedia'] = "'http://service.bfast.com/bfast/click?bfmid=26917872&siteid=41635796&bfpage=deeplink&GOTO=HOTSEARCH&CityName=' +destination + '&InDate=' +startDate + '&OutDate=' +endDate+ '&NumAdult=1'";
    hotelBookingUrl['hotwire'] = "'http://click.linksynergy.com/fs-bin/statform?id=8KQyBpa6sz8&offerid=113245&offerid=93322&bnid=509&subid=0&type=4&inputId=hotel-index&destCity=' +destination + '&endDate=' +endDate + '&startDate=' +startDate";
    hotelBookingUrl['travelocity'] = "'http://travel.travelocity.com/hotel/HotelCobrand.do?Service=TRAVELOCITY&searchMode=city&expr_path=Y&dateFormat=mm%2Fdd%2Fyyyy&city='+destination+'&leavingDate='+startDate+'&returningDate='+endDate+'&numRooms=1&adult1=1&aid=10398279&pid=2124264&url=http://travel.travelocity.com/hotel/HotelCobrand.do'";
    hotelBookingUrl['kayak'] = "'http://www.kayak.com/s/search/hotel?id=8KQyBpa6sz8&offerid=100094&type=4&subid=0&ai=8KQyBpa6sz8&pa=1&df=us1&d1=' +startDate + '&d2=' +endDate+ '&crc=' +destination ";
    hotelBookingUrl['priceline'] = "'http://www.jdoqocy.com/interactive?aid=10408266&pid=2124264&ProductID=5&name=pclnbanner_hot&ROOMS=1&url=http://www.priceline.com/qp.asp&city=' +destination + '&CheckInDate=' +startDate + '&CheckOutDate=' +endDate";
    hotelBookingUrl['orbitz'] = "'http://www.orbitz.com/App/ValidateHotelSearch?id=8KQyBpa6sz8&offerid=66478&type=4&subid=&keywordPIB=' +destination + '&monthInPIB=' + startMonthYear + '&dayInPIB=' + startDay + '&monthOutPIB=' + endMonthYear + '&dayOutPIB=' +endDay + '&numberOfGuestsPIB=1&numberOfRoomsPIB=1'";
    try{
    hotelLink = eval(hotelBookingUrl[bookingSite]);
    }catch(e){alert(e.description);}
    javascript : urchinTracker(hotelLink);
    return hotelLink;
}

function downloadPlugin(){
    if (!window.ttPluginVersion) {
      if (document.all) 
        url = '/i/plugin/setup.msi';
      else
        url = '/i/plugin/tt.xpi';
      window.location.href = url;
      return true;
    }
    return false;
}

function highlightMenu(menu) {
   menu = $(menu);
   if (menu.className.indexOf("highMenu") > 0)
    return;
   menu.className = menu.className + " highMenu";
}

function lowlightMenu(menu) {
   menu = $(menu);
   if (menu.className.indexOf("highMenu") == -1)
    return;
   menu.className = menu.className.replace(" highMenu","");
}

function selectMenu(menu) {
   menu = $(menu);
   if (menu.className.indexOf("selectedMenu") > 0)
    return;
   menu.className = menu.className + " selectedMenu";
}

function unselectMenu(menu) {
   menu = $(menu);
   if (menu.className.indexOf("selectedMenu") == -1)
    return;
   menu.className = menu.className.replace(" selectedMenu","");
}
function showChecklist() {
    var leftPos = 749;
    var myWidth = 0;
    if( typeof( window.innerWidth ) == 'number' ) {
        //Non-IE
        myWidth = window.innerWidth;
    } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
        //IE 6+ in 'standards compliant mode'
        myWidth = document.documentElement.clientWidth;
    } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
        //IE 4 compatible
        myWidth = document.body.clientWidth;
    }
    if (myWidth-170 > 990) leftPos=990;
    $('checkList').style.left = leftPos+"px";
/*
    win = new Window('checklistWindow', {className: "dialog",  zIndex: 100, left: leftPos, top: 130,  width:260, height:220, resizable: false, title: "", draggable:true, minimizable:true, maximizable:false});
    win.getContent().innerHTML = $('checkList').innerHTML;
    win.setDestroyOnClose();
    win.show();
*/    
}

function commonOnLoad() {
    if (document.getElementById("notice"))
       animateNotice();
    if (document.getElementById("map"))
       if (loadMap) loadMap();
}

function validatePassword() {
  var pass=$('password');
  var confirm=$('password_confirm');
  var err = $('activateGuestErrorMessage');
  err.innerHTML = '<br/>';
  if (pass.value != confirm.value) {
    err.innerHTML = 'Passwords do not match';
    new Effect.Highlight(err);
    return false;
  }
  if (pass.value.length < 5) {
    err.innerHTML = 'Password must be at least 5 characters';
    new Effect.Highlight(err);
    return false;
  }
  return true;
}

function validateCompleteAccount() {
  $('password_confirm').value = $('password').value;
  return validatePassword() && validCityElement('origin_city', false, 'Home City', function(msg){var err=$('activateGuestErrorMessage');err.innerHTML=msg;new Effect.Highlight(err);});
}

function createGroup(field) {
    clearInfoField(field);
    field.value = field.value.trim();
    if (field.value == "") {
        alert("Enter group name to create.");
        field.focus();
    } else 
        xajax_createGroup(field.value);
}
function showMenu(e, anchor, id) {
  var clipPosition = Position.cumulativeOffset($(anchor))
  var div = $(id);
  var posX = clipPosition[0]-215;
  var posY = clipPosition[1]-210;
  if (div) {
     div.style.top = posY+"px";
     div.style.left = posX+"px";
     if (!div.getAttribute('eventAdded')) {
        div.setAttribute('eventAdded', 'done');
        div.parentNode.removeChild(div);
        document.body.appendChild(div);
        addEvent(div, "mouseover", function(e){ if (!e) var e=window.event; if (e.target) var trg = e.target; else if (e.srcElement) var trg = e.srcElement;if (trg != div) return; div.hide();});
        addEvent(div, "mouseup", function(e){div.hide();});
     }
     div.show();
     return;
  }
}
function showMainMenu(e, anchor, id) {
  var clipPosition = Position.cumulativeOffset($(anchor))
  var div = $(id);
  var posX = clipPosition[0]-215;
  var posY = clipPosition[1]-200;
  if (div) {
     div.style.top = posY+"px";
     div.style.left = posX+"px";
     if (!div.getAttribute('eventAdded')) {
        div.setAttribute('eventAdded', 'done');
        div.parentNode.removeChild(div);
        document.body.appendChild(div);
        addEvent(div, "mouseover", function(e){ if (!e) var e=window.event; if (e.target) var trg = e.target; else if (e.srcElement) var trg = e.srcElement;if (trg != div) return; div.hide();});
        addEvent(div, "mouseup", function(e){div.hide();});
     }
     div.show();
     return;
  }
}

function handleTextEnter(element) {
	var keyPressed = 0;
	if (window.event) keyPressed = window.event.keyCode;  else keyPressed = !event?0:event.which;
	var script = element.getAttribute('onenter'); 
	if (keyPressed == 13 && script && script != '')
		eval(script);
	return false;
}

function urchinTracker(url){
  if (typeof(pageTracker) != 'undefined' && pageTracker) 
     pageTracker._trackPageview(url);
}

addEvent(window, 'load', commonOnLoad)
var callInProgress = false;
function toggleImageNext(type) {
	toggleImageStart(1, type);
}

function toggleImagePrev(type) {
	toggleImageStart(-1, type);
}

function toggleImageStart(direction, type) {
    if (callInProgress) return;
    callInProgress = true;
	var entity_id = $('entity_id').value;
	xajax_getNextPicture(entity_id, direction, type);
}

var button_next_onclick = toggleImageNext;
var button_prev_onclick = toggleImagePrev;

function toggleImageFinish(image_name) {
	if (!image_name.length) {
		alert("Bad image name");
		return;
	}

	$('entityPhoto').src = image_name;

    callInProgress = false;
}

function toggleImageButtons() {
	if ($('toggle_image_buttons').style.display == 'none') {
		$('toggle_image_buttons').style.display = "";
		$('SWFUpload').style.display = "none";
		if ($('image_upload_status'))
    		$('image_upload_status').style.display = "none";
	} else {
		$('toggle_image_buttons').style.display = "none";
		$('SWFUpload').style.display = "";
	}
	if ($('image_upload_status'))
       	$('image_upload_status').innerHTML = "";
}

function imageUploadSuccessHandler(image_name) {
//	$('entityPhoto').src = image_name;
	$('image_upload_status').innerHTML = "Done!";
	setTimeout('toggleImageButtons()', 100);
}

function imageUploadFailureHandler(message) {
	$('image_upload_status').innerHTML = "Error!";
	alert(message);
}

function file_dialog_complete_function(selected, queued, totalInQueue) {
	if (totalInQueue > 0) {
		this.startUpload();
	}
}

// Default upload start function.
uploadStart = function(fileObj) {
	$("filesDisplay").style.display = "block";

	var li = document.createElement("li");
	var txt = document.createTextNode(fileObj.name);

	li.className = "uploading";
	li.id = fileObj.name;

	var prg = document.createElement("span");
	prg.id = fileObj.name + "progress";
	prg.className = "progressBar"

	li.appendChild(txt);
	li.appendChild(prg);

	$("mmUploadFileListing").appendChild(li);

}

uploadProgress = function(fileObj, bytesLoaded) {
	var progress = $(fileObj.name + "progress");
	var percent = Math.ceil((bytesLoaded / fileObj.size) * 100)
	progress.style.background = "url(/i/images/uploadprogressbar.png) no-repeat -" + (170-Math.ceil(percent*170/100)) + "px 0";
}

uploadComplete = function(fileObj) {
	$(fileObj.name).className = "uploadDone";
	$(fileObj.name).innerHTML += " " + (Math.ceil(fileObj.size / 1000)) + " kb";

	if (this.getStats().files_queued == 0)
		uploadQueueComplete();
	else
		this.startUpload();

}

uploadQueueComplete = function() {
    $("mmUploadFileListing").innerHTML = "";
	$("filesDisplay").style.display = "none";
	$("filesDisplay").innerHTML = "";
	refreshEntityImage();
	setTimeout('toggleImageButtons()', 100);
}

uploadCancel = function() {
}

uploadError = function() {
  for (var i=0;i<arguments.length;i++) {
     alert(arguments[i]);
  }
}
/**
 * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
 *
 * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/,  http://www.vinterwebb.se/
 *
 * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */


/* ******************* */
/* Constructor & Init  */
/* ******************* */
var SWFUpload;

if (SWFUpload == undefined) {
	SWFUpload = function (settings) {
		this.initSWFUpload(settings);
	};
}

SWFUpload.prototype.initSWFUpload = function (settings) {
	try {
		this.customSettings = {};	// A container where developers can place their own settings associated with this instance.
		this.settings = settings;
		this.eventQueue = [];
		this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
		this.movieElement = null;


		// Setup global control tracking
		SWFUpload.instances[this.movieName] = this;

		// Load the settings.  Load the Flash movie.
		this.initSettings();
		this.loadFlash();
		this.displayDebugInfo();
	} catch (ex) {
		delete SWFUpload.instances[this.movieName];
		throw ex;
	}
};

/* *************** */
/* Static Members  */
/* *************** */
SWFUpload.instances = {};
SWFUpload.movieCount = 0;
SWFUpload.version = "2.2.0 2009-03-25";
SWFUpload.QUEUE_ERROR = {
	QUEUE_LIMIT_EXCEEDED	  		: -100,
	FILE_EXCEEDS_SIZE_LIMIT  		: -110,
	ZERO_BYTE_FILE			  		: -120,
	INVALID_FILETYPE		  		: -130
};
SWFUpload.UPLOAD_ERROR = {
	HTTP_ERROR				  		: -200,
	MISSING_UPLOAD_URL	      		: -210,
	IO_ERROR				  		: -220,
	SECURITY_ERROR			  		: -230,
	UPLOAD_LIMIT_EXCEEDED	  		: -240,
	UPLOAD_FAILED			  		: -250,
	SPECIFIED_FILE_ID_NOT_FOUND		: -260,
	FILE_VALIDATION_FAILED	  		: -270,
	FILE_CANCELLED			  		: -280,
	UPLOAD_STOPPED					: -290
};
SWFUpload.FILE_STATUS = {
	QUEUED		 : -1,
	IN_PROGRESS	 : -2,
	ERROR		 : -3,
	COMPLETE	 : -4,
	CANCELLED	 : -5
};
SWFUpload.BUTTON_ACTION = {
	SELECT_FILE  : -100,
	SELECT_FILES : -110,
	START_UPLOAD : -120
};
SWFUpload.CURSOR = {
	ARROW : -1,
	HAND : -2
};
SWFUpload.WINDOW_MODE = {
	WINDOW : "window",
	TRANSPARENT : "transparent",
	OPAQUE : "opaque"
};

// Private: takes a URL, determines if it is relative and converts to an absolute URL
// using the current site. Only processes the URL if it can, otherwise returns the URL untouched
SWFUpload.completeURL = function(url) {
	if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
		return url;
	}
	
	var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
	
	var indexSlash = window.location.pathname.lastIndexOf("/");
	if (indexSlash <= 0) {
		path = "/";
	} else {
		path = window.location.pathname.substr(0, indexSlash) + "/";
	}
	
	return /*currentURL +*/ path + url;
	
};


/* ******************** */
/* Instance Members  */
/* ******************** */

// Private: initSettings ensures that all the
// settings are set, getting a default value if one was not assigned.
SWFUpload.prototype.initSettings = function () {
	this.ensureDefault = function (settingName, defaultValue) {
		this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
	};
	
	// Upload backend settings
	this.ensureDefault("upload_url", "");
	this.ensureDefault("preserve_relative_urls", false);
	this.ensureDefault("file_post_name", "Filedata");
	this.ensureDefault("post_params", {});
	this.ensureDefault("use_query_string", false);
	this.ensureDefault("requeue_on_error", false);
	this.ensureDefault("http_success", []);
	this.ensureDefault("assume_success_timeout", 0);
	
	// File Settings
	this.ensureDefault("file_types", "*.*");
	this.ensureDefault("file_types_description", "All Files");
	this.ensureDefault("file_size_limit", 0);	// Default zero means "unlimited"
	this.ensureDefault("file_upload_limit", 0);
	this.ensureDefault("file_queue_limit", 0);

	// Flash Settings
	this.ensureDefault("flash_url", "swfupload.swf");
	this.ensureDefault("prevent_swf_caching", true);
	
	// Button Settings
	this.ensureDefault("button_image_url", "");
	this.ensureDefault("button_width", 1);
	this.ensureDefault("button_height", 1);
	this.ensureDefault("button_text", "");
	this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
	this.ensureDefault("button_text_top_padding", 0);
	this.ensureDefault("button_text_left_padding", 0);
	this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
	this.ensureDefault("button_disabled", false);
	this.ensureDefault("button_placeholder_id", "");
	this.ensureDefault("button_placeholder", null);
	this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
	this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
	
	// Debug Settings
	this.ensureDefault("debug", false);
	this.settings.debug_enabled = this.settings.debug;	// Here to maintain v2 API
	
	// Event Handlers
	this.settings.return_upload_start_handler = this.returnUploadStart;
	this.ensureDefault("swfupload_loaded_handler", null);
	this.ensureDefault("file_dialog_start_handler", null);
	this.ensureDefault("file_queued_handler", null);
	this.ensureDefault("file_queue_error_handler", null);
	this.ensureDefault("file_dialog_complete_handler", null);
	
	this.ensureDefault("upload_start_handler", null);
	this.ensureDefault("upload_progress_handler", null);
	this.ensureDefault("upload_error_handler", null);
	this.ensureDefault("upload_success_handler", null);
	this.ensureDefault("upload_complete_handler", null);
	
	this.ensureDefault("debug_handler", this.debugMessage);

	this.ensureDefault("custom_settings", {});

	// Other settings
	this.customSettings = this.settings.custom_settings;
	
	// Update the flash url if needed
	if (!!this.settings.prevent_swf_caching) {
		this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
	}
	
	if (!this.settings.preserve_relative_urls) {
		//this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url);	// Don't need to do this one since flash doesn't look at it
		this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
		this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
	}
	
	delete this.ensureDefault;
};

// Private: loadFlash replaces the button_placeholder element with the flash movie.
SWFUpload.prototype.loadFlash = function () {
	var targetElement, tempParent;

	// Make sure an element with the ID we are going to use doesn't already exist
	if (document.getElementById(this.movieName) !== null) {
		throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
	}

	// Get the element where we will be placing the flash movie
	targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;

	if (targetElement == undefined) {
		throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
	}

	// Append the container and load the flash
	tempParent = document.createElement("div");
	tempParent.innerHTML = this.getFlashHTML();	// Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
	targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);

	// Fix IE Flash/Form bug
	if (window[this.movieName] == undefined) {
		window[this.movieName] = this.getMovieElement();
	}
	
};

// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
SWFUpload.prototype.getFlashHTML = function () {
	// Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
	return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload">',
				'<param name="wmode" value="', this.settings.button_window_mode, '" />',
				'<param name="movie" value="', this.settings.flash_url, '" />',
				'<param name="quality" value="high" />',
				'<param name="menu" value="false" />',
				'<param name="allowScriptAccess" value="always" />',
				'<param name="flashvars" value="' + this.getFlashVars() + '" />',
				'</object>'].join("");
};

// Private: getFlashVars builds the parameter string that will be passed
// to flash in the flashvars param.
SWFUpload.prototype.getFlashVars = function () {
	// Build a string from the post param object
	var paramString = this.buildParamString();
	var httpSuccessString = this.settings.http_success.join(",");
	
	// Build the parameter string
	return ["movieName=", encodeURIComponent(this.movieName),
			"&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
			"&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
			"&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
			"&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
			"&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
			"&amp;params=", encodeURIComponent(paramString),
			"&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
			"&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
			"&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
			"&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
			"&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
			"&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
			"&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
			"&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
			"&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
			"&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
			"&amp;buttonText=", encodeURIComponent(this.settings.button_text),
			"&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
			"&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
			"&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
			"&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
			"&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
			"&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
		].join("");
};

// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
// The element is cached after the first lookup
SWFUpload.prototype.getMovieElement = function () {
	if (this.movieElement == undefined) {
		this.movieElement = document.getElementById(this.movieName);
	}

	if (this.movieElement === null) {
		throw "Could not find Flash element";
	}
	
	return this.movieElement;
};

// Private: buildParamString takes the name/value pairs in the post_params setting object
// and joins them up in to a string formatted "name=value&amp;name=value"
SWFUpload.prototype.buildParamString = function () {
	var postParams = this.settings.post_params; 
	var paramStringPairs = [];

	if (typeof(postParams) === "object") {
		for (var name in postParams) {
			if (postParams.hasOwnProperty(name)) {
				paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
			}
		}
	}

	return paramStringPairs.join("&amp;");
};

// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
// all references to the SWF, and other objects so memory is properly freed.
// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
// Credits: Major improvements provided by steffen
SWFUpload.prototype.destroy = function () {
	try {
		// Make sure Flash is done before we try to remove it
		this.cancelUpload(null, false);
		

		// Remove the SWFUpload DOM nodes
		var movieElement = null;
		movieElement = this.getMovieElement();
		
		if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
			// Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
			for (var i in movieElement) {
				try {
					if (typeof(movieElement[i]) === "function") {
						movieElement[i] = null;
					}
				} catch (ex1) {}
			}

			// Remove the Movie Element from the page
			try {
				movieElement.parentNode.removeChild(movieElement);
			} catch (ex) {}
		}
		
		// Remove IE form fix reference
		window[this.movieName] = null;

		// Destroy other references
		SWFUpload.instances[this.movieName] = null;
		delete SWFUpload.instances[this.movieName];

		this.movieElement = null;
		this.settings = null;
		this.customSettings = null;
		this.eventQueue = null;
		this.movieName = null;
		
		
		return true;
	} catch (ex2) {
		return false;
	}
};


// Public: displayDebugInfo prints out settings and configuration
// information about this SWFUpload instance.
// This function (and any references to it) can be deleted when placing
// SWFUpload in production.
SWFUpload.prototype.displayDebugInfo = function () {
	this.debug(
		[
			"---SWFUpload Instance Info---\n",
			"Version: ", SWFUpload.version, "\n",
			"Movie Name: ", this.movieName, "\n",
			"Settings:\n",
			"\t", "upload_url:               ", this.settings.upload_url, "\n",
			"\t", "flash_url:                ", this.settings.flash_url, "\n",
			"\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
			"\t", "requeue_on_error:         ", this.settings.requeue_on_error.toString(), "\n",
			"\t", "http_success:             ", this.settings.http_success.join(", "), "\n",
			"\t", "assume_success_timeout:   ", this.settings.assume_success_timeout, "\n",
			"\t", "file_post_name:           ", this.settings.file_post_name, "\n",
			"\t", "post_params:              ", this.settings.post_params.toString(), "\n",
			"\t", "file_types:               ", this.settings.file_types, "\n",
			"\t", "file_types_description:   ", this.settings.file_types_description, "\n",
			"\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
			"\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
			"\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
			"\t", "debug:                    ", this.settings.debug.toString(), "\n",

			"\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",

			"\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
			"\t", "button_placeholder:       ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
			"\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
			"\t", "button_width:             ", this.settings.button_width.toString(), "\n",
			"\t", "button_height:            ", this.settings.button_height.toString(), "\n",
			"\t", "button_text:              ", this.settings.button_text.toString(), "\n",
			"\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
			"\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
			"\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
			"\t", "button_action:            ", this.settings.button_action.toString(), "\n",
			"\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",

			"\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
			"Event Handlers:\n",
			"\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
			"\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
			"\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
			"\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
			"\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
			"\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
			"\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
			"\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
			"\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
			"\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
		].join("")
	);
};

/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
	the maintain v2 API compatibility
*/
// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
SWFUpload.prototype.addSetting = function (name, value, default_value) {
    if (value == undefined) {
        return (this.settings[name] = default_value);
    } else {
        return (this.settings[name] = value);
	}
};

// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
SWFUpload.prototype.getSetting = function (name) {
    if (this.settings[name] != undefined) {
        return this.settings[name];
	}

    return "";
};



// Private: callFlash handles function calls made to the Flash element.
// Calls are made with a setTimeout for some functions to work around
// bugs in the ExternalInterface library.
SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
	argumentArray = argumentArray || [];
	
	var movieElement = this.getMovieElement();
	var returnValue, returnString;

	// Flash's method if calling ExternalInterface methods (code adapted from MooTools).
	try {
		returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
		returnValue = eval(returnString);
	} catch (ex) {
		throw "Call to " + functionName + " failed";
	}
	
	// Unescape file post param values
	if (returnValue != undefined && typeof returnValue.post === "object") {
		returnValue = this.unescapeFilePostParams(returnValue);
	}

	return returnValue;
};

/* *****************************
	-- Flash control methods --
	Your UI should use these
	to operate SWFUpload
   ***************************** */

// WARNING: this function does not work in Flash Player 10
// Public: selectFile causes a File Selection Dialog window to appear.  This
// dialog only allows 1 file to be selected.
SWFUpload.prototype.selectFile = function () {
	this.callFlash("SelectFile");
};

// WARNING: this function does not work in Flash Player 10
// Public: selectFiles causes a File Selection Dialog window to appear/ This
// dialog allows the user to select any number of files
// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
// for this bug.
SWFUpload.prototype.selectFiles = function () {
	this.callFlash("SelectFiles");
};


// Public: startUpload starts uploading the first file in the queue unless
// the optional parameter 'fileID' specifies the ID 
SWFUpload.prototype.startUpload = function (fileID) {
	this.callFlash("StartUpload", [fileID]);
};

// Public: cancelUpload cancels any queued file.  The fileID parameter may be the file ID or index.
// If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) {
	if (triggerErrorEvent !== false) {
		triggerErrorEvent = true;
	}
	this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
};

// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
// If nothing is currently uploading then nothing happens.
SWFUpload.prototype.stopUpload = function () {
	this.callFlash("StopUpload");
};

/* ************************
 * Settings methods
 *   These methods change the SWFUpload settings.
 *   SWFUpload settings should not be changed directly on the settings object
 *   since many of the settings need to be passed to Flash in order to take
 *   effect.
 * *********************** */

// Public: getStats gets the file statistics object.
SWFUpload.prototype.getStats = function () {
	return this.callFlash("GetStats");
};

// Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
// change the statistics but you can.  Changing the statistics does not
// affect SWFUpload accept for the successful_uploads count which is used
// by the upload_limit setting to determine how many files the user may upload.
SWFUpload.prototype.setStats = function (statsObject) {
	this.callFlash("SetStats", [statsObject]);
};

// Public: getFile retrieves a File object by ID or Index.  If the file is
// not found then 'null' is returned.
SWFUpload.prototype.getFile = function (fileID) {
	if (typeof(fileID) === "number") {
		return this.callFlash("GetFileByIndex", [fileID]);
	} else {
		return this.callFlash("GetFile", [fileID]);
	}
};

// Public: addFileParam sets a name/value pair that will be posted with the
// file specified by the Files ID.  If the name already exists then the
// exiting value will be overwritten.
SWFUpload.prototype.addFileParam = function (fileID, name, value) {
	return this.callFlash("AddFileParam", [fileID, name, value]);
};

// Public: removeFileParam removes a previously set (by addFileParam) name/value
// pair from the specified file.
SWFUpload.prototype.removeFileParam = function (fileID, name) {
	this.callFlash("RemoveFileParam", [fileID, name]);
};

// Public: setUploadUrl changes the upload_url setting.
SWFUpload.prototype.setUploadURL = function (url) {
	this.settings.upload_url = url.toString();
	this.callFlash("SetUploadURL", [url]);
};

// Public: setPostParams changes the post_params setting
SWFUpload.prototype.setPostParams = function (paramsObject) {
	this.settings.post_params = paramsObject;
	this.callFlash("SetPostParams", [paramsObject]);
};

// Public: addPostParam adds post name/value pair.  Each name can have only one value.
SWFUpload.prototype.addPostParam = function (name, value) {
	this.settings.post_params[name] = value;
	this.callFlash("SetPostParams", [this.settings.post_params]);
};

// Public: removePostParam deletes post name/value pair.
SWFUpload.prototype.removePostParam = function (name) {
	delete this.settings.post_params[name];
	this.callFlash("SetPostParams", [this.settings.post_params]);
};

// Public: setFileTypes changes the file_types setting and the file_types_description setting
SWFUpload.prototype.setFileTypes = function (types, description) {
	this.settings.file_types = types;
	this.settings.file_types_description = description;
	this.callFlash("SetFileTypes", [types, description]);
};

// Public: setFileSizeLimit changes the file_size_limit setting
SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
	this.settings.file_size_limit = fileSizeLimit;
	this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
};

// Public: setFileUploadLimit changes the file_upload_limit setting
SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
	this.settings.file_upload_limit = fileUploadLimit;
	this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
};

// Public: setFileQueueLimit changes the file_queue_limit setting
SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
	this.settings.file_queue_limit = fileQueueLimit;
	this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
};

// Public: setFilePostName changes the file_post_name setting
SWFUpload.prototype.setFilePostName = function (filePostName) {
	this.settings.file_post_name = filePostName;
	this.callFlash("SetFilePostName", [filePostName]);
};

// Public: setUseQueryString changes the use_query_string setting
SWFUpload.prototype.setUseQueryString = function (useQueryString) {
	this.settings.use_query_string = useQueryString;
	this.callFlash("SetUseQueryString", [useQueryString]);
};

// Public: setRequeueOnError changes the requeue_on_error setting
SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
	this.settings.requeue_on_error = requeueOnError;
	this.callFlash("SetRequeueOnError", [requeueOnError]);
};

// Public: setHTTPSuccess changes the http_success setting
SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) {
	if (typeof http_status_codes === "string") {
		http_status_codes = http_status_codes.replace(" ", "").split(",");
	}
	
	this.settings.http_success = http_status_codes;
	this.callFlash("SetHTTPSuccess", [http_status_codes]);
};

// Public: setHTTPSuccess changes the http_success setting
SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) {
	this.settings.assume_success_timeout = timeout_seconds;
	this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
};

// Public: setDebugEnabled changes the debug_enabled setting
SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
	this.settings.debug_enabled = debugEnabled;
	this.callFlash("SetDebugEnabled", [debugEnabled]);
};

// Public: setButtonImageURL loads a button image sprite
SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
	if (buttonImageURL == undefined) {
		buttonImageURL = "";
	}
	
	this.settings.button_image_url = buttonImageURL;
	this.callFlash("SetButtonImageURL", [buttonImageURL]);
};

// Public: setButtonDimensions resizes the Flash Movie and button
SWFUpload.prototype.setButtonDimensions = function (width, height) {
	this.settings.button_width = width;
	this.settings.button_height = height;
	
	var movie = this.getMovieElement();
	if (movie != undefined) {
		movie.style.width = width + "px";
		movie.style.height = height + "px";
	}
	
	this.callFlash("SetButtonDimensions", [width, height]);
};
// Public: setButtonText Changes the text overlaid on the button
SWFUpload.prototype.setButtonText = function (html) {
	this.settings.button_text = html;
	this.callFlash("SetButtonText", [html]);
};
// Public: setButtonTextPadding changes the top and left padding of the text overlay
SWFUpload.prototype.setButtonTextPadding = function (left, top) {
	this.settings.button_text_top_padding = top;
	this.settings.button_text_left_padding = left;
	this.callFlash("SetButtonTextPadding", [left, top]);
};

// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
SWFUpload.prototype.setButtonTextStyle = function (css) {
	this.settings.button_text_style = css;
	this.callFlash("SetButtonTextStyle", [css]);
};
// Public: setButtonDisabled disables/enables the button
SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
	this.settings.button_disabled = isDisabled;
	this.callFlash("SetButtonDisabled", [isDisabled]);
};
// Public: setButtonAction sets the action that occurs when the button is clicked
SWFUpload.prototype.setButtonAction = function (buttonAction) {
	this.settings.button_action = buttonAction;
	this.callFlash("SetButtonAction", [buttonAction]);
};

// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
SWFUpload.prototype.setButtonCursor = function (cursor) {
	this.settings.button_cursor = cursor;
	this.callFlash("SetButtonCursor", [cursor]);
};

/* *******************************
	Flash Event Interfaces
	These functions are used by Flash to trigger the various
	events.
	
	All these functions a Private.
	
	Because the ExternalInterface library is buggy the event calls
	are added to a queue and the queue then executed by a setTimeout.
	This ensures that events are executed in a determinate order and that
	the ExternalInterface bugs are avoided.
******************************* */

SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
	// Warning: Don't call this.debug inside here or you'll create an infinite loop
	
	if (argumentArray == undefined) {
		argumentArray = [];
	} else if (!(argumentArray instanceof Array)) {
		argumentArray = [argumentArray];
	}
	
	var self = this;
	if (typeof this.settings[handlerName] === "function") {
		// Queue the event
		this.eventQueue.push(function () {
			this.settings[handlerName].apply(this, argumentArray);
		});
		
		// Execute the next queued event
		setTimeout(function () {
			self.executeNextEvent();
		}, 0);
		
	} else if (this.settings[handlerName] !== null) {
		throw "Event handler " + handlerName + " is unknown or is not a function";
	}
};

// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
// we must queue them in order to garentee that they are executed in order.
SWFUpload.prototype.executeNextEvent = function () {
	// Warning: Don't call this.debug inside here or you'll create an infinite loop

	var  f = this.eventQueue ? this.eventQueue.shift() : null;
	if (typeof(f) === "function") {
		f.apply(this);
	}
};

// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
// properties that contain characters that are not valid for JavaScript identifiers. To work around this
// the Flash Component escapes the parameter names and we must unescape again before passing them along.
SWFUpload.prototype.unescapeFilePostParams = function (file) {
	var reg = /[$]([0-9a-f]{4})/i;
	var unescapedPost = {};
	var uk;

	if (file != undefined) {
		for (var k in file.post) {
			if (file.post.hasOwnProperty(k)) {
				uk = k;
				var match;
				while ((match = reg.exec(uk)) !== null) {
					uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
				}
				unescapedPost[uk] = file.post[k];
			}
		}

		file.post = unescapedPost;
	}

	return file;
};

// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
SWFUpload.prototype.testExternalInterface = function () {
	try {
		return this.callFlash("TestExternalInterface");
	} catch (ex) {
		return false;
	}
};

// Private: This event is called by Flash when it has finished loading. Don't modify this.
// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
SWFUpload.prototype.flashReady = function () {
	// Check that the movie element is loaded correctly with its ExternalInterface methods defined
	var movieElement = this.getMovieElement();

	if (!movieElement) {
		this.debug("Flash called back ready but the flash movie can't be found.");
		return;
	}

	this.cleanUp(movieElement);
	
	this.queueEvent("swfupload_loaded_handler");
};

// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
// This function is called by Flash each time the ExternalInterface functions are created.
SWFUpload.prototype.cleanUp = function (movieElement) {
	// Pro-actively unhook all the Flash functions
	try {
		if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
			this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
			for (var key in movieElement) {
				try {
					if (typeof(movieElement[key]) === "function") {
						movieElement[key] = null;
					}
				} catch (ex) {
				}
			}
		}
	} catch (ex1) {
	
	}

	// Fix Flashes own cleanup code so if the SWFMovie was removed from the page
	// it doesn't display errors.
	window["__flash__removeCallback"] = function (instance, name) {
		try {
			if (instance) {
				instance[name] = null;
			}
		} catch (flashEx) {
		
		}
	};

};


/* This is a chance to do something before the browse window opens */
SWFUpload.prototype.fileDialogStart = function () {
	this.queueEvent("file_dialog_start_handler");
};


/* Called when a file is successfully added to the queue. */
SWFUpload.prototype.fileQueued = function (file) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("file_queued_handler", file);
};


/* Handle errors that occur when an attempt to queue a file fails. */
SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
};

/* Called after the file dialog has closed and the selected files have been queued.
	You could call startUpload here if you want the queued files to begin uploading immediately. */
SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) {
	this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
};

SWFUpload.prototype.uploadStart = function (file) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("return_upload_start_handler", file);
};

SWFUpload.prototype.returnUploadStart = function (file) {
	var returnValue;
	if (typeof this.settings.upload_start_handler === "function") {
		file = this.unescapeFilePostParams(file);
		returnValue = this.settings.upload_start_handler.call(this, file);
	} else if (this.settings.upload_start_handler != undefined) {
		throw "upload_start_handler must be a function";
	}

	// Convert undefined to true so if nothing is returned from the upload_start_handler it is
	// interpretted as 'true'.
	if (returnValue === undefined) {
		returnValue = true;
	}
	
	returnValue = !!returnValue;
	
	this.callFlash("ReturnUploadStart", [returnValue]);
};



SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
};

SWFUpload.prototype.uploadError = function (file, errorCode, message) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_error_handler", [file, errorCode, message]);
};

SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
};

SWFUpload.prototype.uploadComplete = function (file) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_complete_handler", file);
};

/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
   internal debug console.  You can override this event and have messages written where you want. */
SWFUpload.prototype.debug = function (message) {
	this.queueEvent("debug_handler", message);
};


/* **********************************
	Debug Console
	The debug console is a self contained, in page location
	for debug message to be sent.  The Debug Console adds
	itself to the body if necessary.

	The console is automatically scrolled as messages appear.
	
	If you are using your own debug handler or when you deploy to production and
	have debug disabled you can remove these functions to reduce the file size
	and complexity.
********************************** */
   
// Private: debugMessage is the default debug_handler.  If you want to print debug messages
// call the debug() function.  When overriding the function your own function should
// check to see if the debug setting is true before outputting debug information.
SWFUpload.prototype.debugMessage = function (message) {
	if (this.settings.debug) {
		var exceptionMessage, exceptionValues = [];

		// Check for an exception object and print it nicely
		if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
			for (var key in message) {
				if (message.hasOwnProperty(key)) {
					exceptionValues.push(key + ": " + message[key]);
				}
			}
			exceptionMessage = exceptionValues.join("\n") || "";
			exceptionValues = exceptionMessage.split("\n");
			exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
			SWFUpload.Console.writeLine(exceptionMessage);
		} else {
			SWFUpload.Console.writeLine(message);
		}
	}
};

SWFUpload.Console = {};
SWFUpload.Console.writeLine = function (message) {
	var console, documentForm;

	try {
		console = document.getElementById("SWFUpload_Console");

		if (!console) {
			documentForm = document.createElement("form");
			document.getElementsByTagName("body")[0].appendChild(documentForm);

			console = document.createElement("textarea");
			console.id = "SWFUpload_Console";
			console.style.fontFamily = "monospace";
			console.setAttribute("wrap", "off");
			console.wrap = "off";
			console.style.overflow = "auto";
			console.style.width = "700px";
			console.style.height = "350px";
			console.style.margin = "5px";
			documentForm.appendChild(console);
		}

		console.value += message + "\n";

		console.scrollTop = console.scrollHeight - console.clientHeight;
	} catch (ex) {
		alert("Exception: " + ex.name + " Message: " + ex.message);
	}
};

