

var ActionHandlers = {
	
	contentLoaded : function (content) {
		var html = content[0];
		var js = content[1];
		document.getElementById ('content').innerHTML = html;
		eval (js);
	}
	
};

function elementHasClass (element, className) {
    var re = new RegExp ('(?:^|\\s+)' + className + '(?:\\s+|$)');
    return re.test (element.className);
}


var Delegate = function (thisObject, method) {
	if (typeof method == 'function') {
		return function() {
			method.apply (thisObject, arguments);
		}
	} else {
    	return function() {
	        thisObject[method].apply (thisObject, arguments);
    	}	
	}
}

isObjectEmpty = function (object) {
	for (var i in object) return false;
	return true;
}

//todo - get it out of here
var Tabs = {
	open : function (tab, event) {
		tab = event.target || event.srcElement;
		var tabParent = tab.parentNode;
		for (var i = 0; i < tabParent.childNodes.length; i++) {
			var node = tabParent.childNodes[i];
			if (node.nodeType == 1) {
				node.className = node == tab ? 'DialogTabCurrent' : '';
			}
		}
		Tabs.doOpenTab (document.getElementById (tab.id.replace ("_button", "_tab")));
	},
	doOpenTab : function (tabContentElement) {
		var tabContentParent = tabContentElement.parentNode;
		for (var i = 0; i < tabContentParent.childNodes.length; i++) {
			var node = tabContentParent.childNodes[i];
			if (node.nodeType == 1) {
				node.style.display = node == tabContentElement ? 'block' : 'none';
			}
		}
	}
}

String.prototype.trim = function() { return this.replace( /^\s+|\s+$/, "" );}

removeCSSClass = function (elem, className) {
	elem.className = elem.className.replace (className, "").trim();
} 

addCSSClass = function (elem, className) {
	this.removeCSSClass (elem, className);
	elem.className = (elem.className + " " + className).trim();
}

focusFirstInput = function (parent) {
	if (typeof parent == 'string') parent = document.getElementById (parent);
	var inputs = parent.getElementsByTagName ('INPUT');
	for (var i = 0; i < inputs.length; i++) {
		var i = inputs[i];
		if ((!i.disabled) && (i.type == 'text' || i.type == 'password' || i.type == 'radio' || i.type == 'checkbox' || i.type == 'file')) {
			i.focus();
			return;
		}
	}
}

function DarkenPage()
{
    var page_screen = document.getElementById('page_screen');
    page_screen.style.height = document.body.parentNode.scrollHeight + 'px';
    page_screen.style.display = 'block';
}

function LightenPage()
{
    var page_screen = document.getElementById('page_screen');
    page_screen.style.display = 'none';
}

function showIntroScreen() {
    var is = document.getElementById('intro_screen');    
    if (!is) return false;    
    
    w = 400;
    h = 300;
        
    xc = Math.round((document.body.clientWidth/2)-(w/2))
    yc = parseInt(document.body.parentNode.scrollTop + (screen.height /2)) - (h);
    is.style.left = xc + "px";
    is.style.top  = yc + "px";
    
    // get the x and y coordinates to center the intro screen panel
    //xc = Math.round((document.body.clientWidth/2)-(w/2))
    //yc = Math.round((document.body.clientHeight/2)-(h/2))    
    // show the intro screen panel
    //is.style.left = xc + "px";
    //is.style.top  = yc + "px";
    
    DarkenPage(); 
    
    is.style.display = 'block';
}

function hideIntroScreen() {
    LightenPage();

    var is = document.getElementById('intro_screen');    
    if (!is) return false;
    
    is.style.display = 'none';   
}

function readGlobalFontSize(){    
    pa_cfs = ReadCookie("bbfontsize");
    return (pa_cfs) ? pa_cfs : 11;
}

function decFontSize(){
    var globalFontSize;
    if (!globalFontSize) globalFontSize = readGlobalFontSize();
    globalFontSize--;
    setFontSize(globalFontSize);
}

function incFontSize(){
    var globalFontSize;
    if (!globalFontSize) globalFontSize = readGlobalFontSize();
    globalFontSize++;
    setFontSize(globalFontSize);
}

function defFontSize(){    
    setFontSize(11);
    SetCookie("bbfontsize",'', -10000);
}

function setFontSize(size){
    if(typeof $ == 'undefined') return false;
    
    if (size > 16) size = 16;
    if (size < 9) size = 9;
    
    var globalFontSize = size; 
    
    var id = 'resize_box'; //class name
        
    $('.resize_box').css('font-size', size+'px');
    $('.resize_box *').css('font-size', size+'px');
        
    SetCookie("bbfontsize",globalFontSize, true); 
}

function baseDomainString()
{
  e = document.domain.split(/\./);
  
  if ( e.length > 1 ) 
  {
    return( "" + e[e.length-2] + "." +  e[e.length-1]) + ";"  ;
  }
  else
  {
    return("");
  }
}

function SetCookie( name, value, expires ) 
{
    var today = new Date();
    today.setTime( today.getTime() );

    var domain = baseDomainString();
    var path = '/';
    var secure = 0;
    
    if ( expires )
    {
        expires = expires * 1000 * 60 * 60 * 24;
    }
    
    var expires_date = new Date( today.getTime() + (expires) );
    
    document.cookie = name + "=" + escape( value ) +
    ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) + 
    ( ( path ) ? ";path=" + path : "" ) + 
    ( ( domain ) ? ";domain=." + domain : "" ) +
    ( ( secure ) ? ";secure" : "" );
}

function ReadCookie( name )
{
    var start = document.cookie.indexOf( name + "=" );
    var len = start + name.length + 1;
    if ( ( !start ) &&
    ( name != document.cookie.substring( 0, name.length ) ) )
    {
    return null;
    }
    if ( start == -1 ) return null;
    var end = document.cookie.indexOf( ";", len );
    if ( end == -1 ) end = document.cookie.length;
    return unescape( document.cookie.substring( len, end ) );
}

function popupImage(href, href1) {
    w = 900;
    h = 800;
    wleft = (screen.availWidth -50 - w) / 2;
    wtop = (screen.availHeight -100 - h) / 2;    

    /*    
    var in_tab = false;
    if (!e) {
        var e = window.event;
    }
    */
    
    var popup = window.open(href+'&popup=1','','status=1,scrollbars=1,width='+w+',height='+h+',resizable=1,screenX='+wleft+',screenY='+wtop);
    if (popup==null || typeof(popup)=="undefined") {
        window.location = href1;
    }
}

function showSendToFriend() {
    stf = document.getElementById('sendto'); 
    if (!stf) return false;    
    h = parseInt(document.body.parentNode.scrollTop + (screen.height /2)) - 600;
    stf.style.top = h+'px';
    stf.style.display = 'block';
}

function sendToFriendSubmit(){
    form = document.getElementById('sendto_form'); 
    
    if (form.elements['friend_email'].value.length ==0 || form.elements['friend_email'].value.trim().length == 0) {
        alert('Please, enter your friend`s email address.'); 
        return false;
    }
    if (form.elements['email'].value.length ==0 || form.elements['email'].value.trim().length == 0) {
        alert('Please, enter your email address.'); 
        return false;
    }
    /*    
    if (form.elements['name'].value.length ==0 || form.elements['name'].value.trim().length == 0) {
        alert('Please, enter your name.'); 
        return false;
    }
    */
    
    var data = {   uri : location.href, 
            id : form.elements['id'].value, 
            title : form.elements['title'].value, 
            email_content : form.elements['email_content'].value,
            friend_email : form.elements['friend_email'].value,
            /*friend_name : form.elements['friend_name'].value,*/
            email : form.elements['email'].value,
            name : form.elements['name'].value
        };
    
    form.elements['send'].disabled = true;
        
    if (form.elements['m'].value == 1) {
    (new Articles_ActionHandler).sendToFriend( data,
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                form = document.getElementById('sendto_form');
                form.elements['friend_email'].value = '';
                document.getElementById('sendto').style.display = 'none';
                alert('Email was sent.');
            } else {
                alert(result);
            }  
        }
    );
    } else if (form.elements['m'].value == 2) {
    (new Reviews_ActionHandler).sendToFriend( data,
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                form = document.getElementById('sendto_form');
                form.elements['friend_email'].value = '';
                document.getElementById('sendto').style.display = 'none';
                alert('Email was sent.');
            } else {
                alert(result);
            }  
        }
    );    
    } else {
    (new Phone_ActionHandler).sendToFriend( data,        
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                form = document.getElementById('sendto_form');
                form.elements['friend_email'].value = '';
                document.getElementById('sendto').style.display = 'none';
                alert('Email was sent.');
            } else {
                alert(result);
            }  
        }
    );    
    }
}

function showSubscribeForm() {
    stf = document.getElementById('subscribe'); 
    if (!stf) return false;    
    h = parseInt(document.body.parentNode.scrollTop + (screen.height /2)) - 400;
    stf.style.top = h+'px';
    stf.style.display = 'block';
}

function subscribeSubmit(){

    var form = document.getElementById('subscribe_form');    
    var module = form.elements['module'].value;
    
    if (form.elements['email'].value.length ==0 || form.elements['email'].value.trim().length == 0) {
        alert('Please, enter your email address.'); 
        return false;
    }    
    
    var data = $('#subscribe_form').formSerialize();
    
    form.elements['send'].disabled = true;
        
    if (module == 'articles') {
    (new Articles_ActionHandler).subscribe( data,
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull. Please, check your email for confirmation instructions.');
            } else if (result === 1) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull.');
            } else {
                alert(result);
            }  
        }
    );
    } else if (module == 'reviews') {
    (new Reviews_ActionHandler).subscribe( data,
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull. Please, check your email for confirmation instructions.');
            } else if (result === 1) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull.');
            } else {
                alert(result);
            }  
        }
    );    
    } else if (module == 'phones'){
    
    (new Phone_ActionHandler).subscribe( data,        
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull. Please, check your email for confirmation instructions.');
            } else if (result === 1) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull.');
            } else {
                alert(result);
            }
        }
    );    
    } else if (module == 'manufacturers'){    
    (new Manufacturers_ActionHandler).subscribe( data,        
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull. Please, check your email for confirmation instructions.');
            } else if (result === 1) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull.');
            } else {
                alert(result);
            }
        }
    );    
    } else if (module == 'providers'){
    (new Providers_ActionHandler).subscribe( data,        
        function(result) {
            form.elements['send'].disabled = false;
            if (result === true) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull. Please, check your email for confirmation instructions.');
            } else if (result === 1) {
                document.getElementById('subscribe').style.display = 'none';
                alert('Subscription was succesfull.');
            } else {
                alert(result);
            }
        }
    );    
    }
}

var globalFontSize = -1;

/**
 * SWFObject v1.4.4: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2006 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * **SWFObject is the SWF embed script formerly known as FlashObject. The name was changed for
 *   legal reasons.
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}
if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}
if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}
deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a,_b){if(!document.getElementById){return;}
this.DETECT_KEY=_b?_b:"detectflash";
this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);
this.params=new Object();
this.variables=new Object();
this.attributes=new Array();
if(_1){this.setAttribute("swf",_1);}
if(id){this.setAttribute("id",id);}
if(w){this.setAttribute("width",w);}
if(h){this.setAttribute("height",h);}
if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}
this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();
if(c){this.addParam("bgcolor",c);}
var q=_8?_8:"high";
this.addParam("quality",q);
this.setAttribute("useExpressInstall",_7);
this.setAttribute("doExpressInstall",false);
var _d=(_9)?_9:window.location;
this.setAttribute("xiRedirectUrl",_d);
this.setAttribute("redirectUrl","");
if(_a){this.setAttribute("redirectUrl",_a);}};
deconcept.SWFObject.prototype={setAttribute:function(_e,_f){
this.attributes[_e]=_f;
},getAttribute:function(_10){
return this.attributes[_10];
},addParam:function(_11,_12){
this.params[_11]=_12;
},getParams:function(){
return this.params;
},addVariable:function(_13,_14){
this.variables[_13]=_14;
},getVariable:function(_15){
return this.variables[_15];
},getVariables:function(){
return this.variables;
},getVariablePairs:function(){
var _16=new Array();
var key;
var _18=this.getVariables();
for(key in _18){_16.push(key+"="+_18[key]);}
return _16;},getSWFHTML:function(){var _19="";
if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){
if(this.getAttribute("doExpressInstall")){
this.addVariable("MMplayerType","PlugIn");}
_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\"";
_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";
var _1a=this.getParams();
for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}
var _1c=this.getVariablePairs().join("&");
if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";
}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");}
_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\">";
_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";
var _1d=this.getParams();
for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}
var _1f=this.getVariablePairs().join("&");
if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}
return _19;
},write:function(_20){
if(this.getAttribute("useExpressInstall")){
var _21=new deconcept.PlayerVersion([6,0,65]);
if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){
this.setAttribute("doExpressInstall",true);
this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));
document.title=document.title.slice(0,47)+" - Flash Player Installation";
this.addVariable("MMdoctitle",document.title);}}
if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){
var n=(typeof _20=="string")?document.getElementById(_20):_20;
n.innerHTML=this.getSWFHTML();return true;
}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}
return false;}};
deconcept.SWFObjectUtil.getPlayerVersion=function(){
var _23=new deconcept.PlayerVersion([0,0,0]);
if(navigator.plugins&&navigator.mimeTypes.length){
var x=navigator.plugins["Shockwave Flash"];
if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}
}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}
catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}
catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}
catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}
return _23;};
deconcept.PlayerVersion=function(_27){
this.major=_27[0]!=null?parseInt(_27[0]):0;
this.minor=_27[1]!=null?parseInt(_27[1]):0;
this.rev=_27[2]!=null?parseInt(_27[2]):0;
};
deconcept.PlayerVersion.prototype.versionIsValid=function(fv){
if(this.major<fv.major){return false;}
if(this.major>fv.major){return true;}
if(this.minor<fv.minor){return false;}
if(this.minor>fv.minor){return true;}
if(this.rev<fv.rev){
return false;
}return true;};
deconcept.util={getRequestParameter:function(_29){
var q=document.location.search||document.location.hash;
if(q){var _2b=q.substring(1).split("&");
for(var i=0;i<_2b.length;i++){
if(_2b[i].substring(0,_2b[i].indexOf("="))==_29){
return _2b[i].substring((_2b[i].indexOf("=")+1));}}}
return "";}};
deconcept.SWFObjectUtil.cleanupSWFs=function(){if(window.opera||!document.all){return;}
var _2d=document.getElementsByTagName("OBJECT");
for(var i=0;i<_2d.length;i++){_2d[i].style.display="none";for(var x in _2d[i]){
if(typeof _2d[i][x]=="function"){_2d[i][x]=function(){};}}}};
deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};
__flash_savedUnloadHandler=function(){};
if(typeof window.onunload=="function"){
var _30=window.onunload;
window.onunload=function(){
deconcept.SWFObjectUtil.cleanupSWFs();_30();};
}else{window.onunload=deconcept.SWFObjectUtil.cleanupSWFs;}};
if(typeof window.onbeforeunload=="function"){
var oldBeforeUnload=window.onbeforeunload;
window.onbeforeunload=function(){
deconcept.SWFObjectUtil.prepUnload();
oldBeforeUnload();};
}else{window.onbeforeunload=deconcept.SWFObjectUtil.prepUnload;}
if(Array.prototype.push==null){
Array.prototype.push=function(_31){
this[this.length]=_31;
return this.length;};}
var getQueryParamValue=deconcept.util.getRequestParameter;
var FlashObject=deconcept.SWFObject;
var SWFObject=deconcept.SWFObject;


/**
* CPAINT - Cross-Platform Asynchronous INterface Toolkit
*
* http://sf.net/projects/cpaint
* 
* released under the terms of the LGPL
* see http://www.fsf.org/licensing/licenses/lgpl.txt for details
*
* @package      CPAINT
* @access       public
* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @author       Paul Sullivan <wiley14@gmail.com>
* @author       Dominique Stender <dstender@st-webdevelopment.de>
* @author		Stephan Tijink <stijink@googlemail.com>
* @version      $Id$
*/
function cpaint() {
  /**
  * CPAINT version
  * 
  * @access     protected
  * @var        string      version
  */
  this.version = '2.0.2';
  
  /**
  * configuration options both for this class but also for  the cpaint_call() objects.
  *
  * @access     protected
  * @var        array       config
  */
  var config                      = new Array();
  config['debugging']             = -1;
  config['proxy_url']             = '';
  config['transfer_mode']         = 'GET';
  config['async']                 = true;
  config['response_type']         = 'OBJECT';
  config['persistent_connection'] = false;
  config['use_cpaint_api']        = true;
  
  /**
  * maintains the next free index in the stack
  *
  * @access   protected
  * @var      integer   stack_count
  */
  var stack_count = 0;

  /**
  * property returns whether or not the browser is AJAX capable
  * 
  * @access		public
  * @return		boolean
  */
  this.capable = test_ajax_capability();
  
  /**
  * switches debug mode on/off.
  *
  * @access   public
  * @param    boolean    debug    debug flag
  * @return   void
  */
  this.set_debug = function() {
    
    if (typeof arguments[0] == 'boolean') {
      if (arguments[0] === true) {
        config['debugging'] = 1;

      } else {
        config['debugging'] = 0;
      }
      
    } else if (typeof arguments[0] == 'number') {
      config['debugging'] = Math.round(arguments[0]);
    }
  }

  /**
  * defines the URL of the proxy script.
  *
  * @access   public
  * @param    string    proxy_url    URL of the proxyscript to connect
  * @return   void
  */
  this.set_proxy_url = function() {
    
    if (typeof arguments[0] == 'string') {

      config['proxy_url'] = arguments[0];
    }
  }

  /**
  * sets the transfer_mode (GET|POST).
  *
  * @access   public
  * @param    string    transfer_mode    transfer_mode
  * @return   void
  */
  this.set_transfer_mode = function() {
    
    if (arguments[0].toUpperCase() == 'GET'
      || arguments[0].toUpperCase() == 'POST') {

      config['transfer_mode'] = arguments[0].toUpperCase();
    }
  }

  /**
  * sets the flag whether or not to use asynchronous calls.
  *
  * @access   public
  * @param    boolean    async    syncronization flag
  * @return   void
  */
  this.set_async = function() {
    
    if (typeof arguments[0] == 'boolean') {
      config['async'] = arguments[0];
    }
  }

  /**
  * defines the response type.
  *
  * allowed values are:
  *   TEXT    = raw text response
  *   XML     = raw XMLHttpObject
  *   OBJECT  = parsed JavaScript object structure from XMLHttpObject
  *
  * the default is OBJECT.
  *
  * @access   public
  * @param    string    response_type    response type
  * @return   void
  */
  this.set_response_type = function() {
    
    if (arguments[0].toUpperCase() == 'TEXT'
      || arguments[0].toUpperCase() == 'XML'
      || arguments[0].toUpperCase() == 'OBJECT'
      || arguments[0].toUpperCase() == 'E4X'
      || arguments[0].toUpperCase() == 'JSON') {

      config['response_type'] = arguments[0].toUpperCase();
    }
  }

  /**
  * sets the flag whether or not to use a persistent connection.
  *
  * @access   public
  * @param    boolean    persistent_connection    persistance flag
  * @return   void
  */
  this.set_persistent_connection = function() {
    
    if (typeof arguments[0] == 'boolean') {
      config['persistent_connection'] = arguments[0];
    }
  }
  
  
  /**
  * sets the flag whether or not to use the cpaint api on the backend.
  *
  * @access    public
  * @param     boolean    cpaint_api      api_flag
  * @return    void
  */
  this.set_use_cpaint_api = function() {
    if (typeof arguments[0] == 'boolean') {
      config['use_cpaint_api'] = arguments[0];
    }
  }
  
  /**
  * tests whether one of the necessary implementations
  * of the XMLHttpRequest class are available
  *
  * @access     protected
  * @return     boolean
  */
  function test_ajax_capability() {
    var cpc = new cpaint_call(0, config, this.version);
    return cpc.test_ajax_capability();
  }

  /**
  * takes the arguments supplied and triggers a call to the CPAINT backend
  * based on the settings.
  *
  * upon response cpaint_call.callback() will automatically be called
  * to perform post-processing operations.
  *
  * @access   public
  * @param    string    url                 remote URL to call
  * @param    string    remote_method       remote method to call
  * @param    object    client_callback     client side callback method to deliver the remote response to. do NOT supply a string!
  * @param    mixed     argN                remote parameters from now on
  * @return   void
  */
  this.call = function() {
    var use_stack = -1;
    
    if (config['persistent_connection'] == true
      && __cpaint_stack[0] != null) {

      switch (__cpaint_stack[0].get_http_state()) {
        case -1:
          // no XMLHttpObject object has already been instanciated
          // create new object and configure it
          use_stack = 0;
          debug('no XMLHttpObject object to re-use for persistence, creating new one later', 2);
          break;
          
        case 4:
          // object is ready for a new request, no need to do anything
          use_stack = 0
          debug('re-using the persistent connection', 2);
          break;
          
        default:
          // connection is currently in use, don't do anything
          debug('the persistent connection is in use - skipping this request', 2);
      }
      
    } else if (config['persistent_connection'] == true) {
      // persistent connection is active, but no object has been instanciated
      use_stack = 0;
      __cpaint_stack[use_stack] = new cpaint_call(use_stack, config, this.version);
      debug('no cpaint_call object available for re-use, created new one', 2);
    
    } else {
      // no connection persistance
      use_stack = stack_count;
      __cpaint_stack[use_stack] = new cpaint_call(use_stack, config, this.version);
      debug('no cpaint_call object created new one', 2);
    }

    // configure cpaint_call if allowed to
    if (use_stack != -1) {
      __cpaint_stack[use_stack].set_client_callback(arguments[2]);
      
      // distribute according to proxy use
      if (config['proxy_url'] != '') {
        __cpaint_stack[use_stack].call_proxy(arguments);
      
      } else {
        __cpaint_stack[use_stack].call_direct(arguments);
      }

      // increase stack counter
      stack_count++;
      debug('stack size: ' + __cpaint_stack.length, 2);
    }
  }

  /**
  * debug method
  *
  * @access  protected
  * @param   string       message         the message to debug
  * @param   integer      debug_level     debug level at which the message appears
  * @return  void
  */
  var debug  = function(message, debug_level) {
    var prefix = '[CPAINT Debug] ';
    
    if (debug_level < 1) {
      prefix = '[CPAINT Error] ';
    }
    
    if (config['debugging'] >= debug_level) {
      alert(prefix + message);
    }
  }
}

/**
* internal FIFO stack of cpaint_call() objects.
*
* @access   protected
* @var      array    __cpaint_stack
*/
var __cpaint_stack = new Array();

/**
* local instance of cpaint_transformer
* MSIE is unable to handle static classes... sheesh.
*
* @access   public
* @var      object    __cpaint_transformer
*/
var __cpaint_transformer = new cpaint_transformer();

/**
* transport agent class
*
* creates the request object, takes care of the response, handles the 
* client callback. Is configured by the cpaint() object.
*
* @package      CPAINT
* @access       public
* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @author       Dominique Stender <dstender@st-webdevelopment.de>
* @author       Paul Sullivan <wiley14@gmail.com>
* @param        integer     stack_id      stack Id in cpaint
* @param        array       config        configuration array for this call
* @param        string      version       CPAINT API version
*/
function cpaint_call() {
  /**
  * CPAINT version
  * 
  * @access     protected
  * @var        string      version
  */
  var version = arguments[2];
  
  /**
  * configuration options both for this class objects.
  *
  * @access     protected
  * @var        array       config
  */
  var config                      = new Array();
  config['debugging']             = arguments[1]['debugging'];
  config['proxy_url']             = arguments[1]['proxy_url'];
  config['transfer_mode']         = arguments[1]['transfer_mode'];
  config['async']                 = arguments[1]['async'];
  config['response_type']         = arguments[1]['response_type'];
  config['persistent_connection'] = arguments[1]['persistent_connection'];
  config['use_cpaint_api']        = arguments[1]['use_cpaint_api'];

  /**
  * XMLHttpObject used for this request.
  *
  * @access   protected
  * @var      object     httpobj
  */
  var httpobj    = false;

  /**
  * client callback function.
  *
  * @access   public
  * @var      function    client_callback
  */
  var client_callback;

  /**
  * stores the stack Id within the cpaint object
  *
  * @access   protected
  * @var      stack_id
  */
  var stack_id = arguments[0];
  
  /**
  * sets the client callback function.
  *
  * @access   public
  * @param    function    client_callback     the client callback function
  * @return   void
  */
  this.set_client_callback = function() {
    
    if (typeof arguments[0] == 'function') {
      client_callback = arguments[0];
    }
  }

  /**
  * returns the ready state of the internal XMLHttpObject
  *
  * if no such object was set up already, -1 is returned
  * 
  * @access     public
  * @return     integer
  */
  this.get_http_state = function() {
    var return_value = -1;
    
    if (typeof httpobj == 'object') {
      return_value = httpobj.readyState;
    }
    
    return return_value;
  }
  
  /**
  * internal method for remote calls to the local server without use of the proxy script.
  *
  * @access   public
  * @param    array    call_arguments    array of arguments initially passed to cpaint.call()
  * @return   void
  */
  this.call_direct = function(call_arguments) {
    var url             = call_arguments[0];
    var remote_method   = call_arguments[1];
    var querystring     = '';
    var i               = 0;
    
    // correct link to self
    if (url == 'SELF') {
      url = document.location.href;
    }
  
    if (config['use_cpaint_api'] == true) {
      // backend uses cpaint api
      // pass parameters to remote method
      for (i = 3; i < call_arguments.length; i++) {

        if ((typeof call_arguments[i] == 'string'
              && call_arguments[i] != ''
              && call_arguments[i].search(/^\s+$/g) == -1)
          && !isNaN(call_arguments[i])
          && isFinite(call_arguments[i])) {
          // numerical value, convert it first
          querystring += '&cpaint_argument[]=' + encodeURIComponent(JSONCP.stringify(Number(call_arguments[i])));
        
        } else {
          querystring += '&cpaint_argument[]=' + encodeURIComponent(JSONCP.stringify(call_arguments[i]));
        }
      }
    
      // add response type to querystring
      querystring += '&cpaint_response_type=' + config['response_type'];
    
      // build header
      if (config['transfer_mode'] == 'GET') {
				
        if(url.indexOf('?') != -1) {
					url = url + '&cpaint_function=' + remote_method +	querystring;
				
        } else {
					url = url + '?cpaint_function=' + remote_method +	querystring; 
				}
      
      } else {
        querystring = 'cpaint_function=' + remote_method + querystring;
      }
      
    } else {
      // backend does not use cpaint api
      // pass parameters to remote method
      for (i = 3; i < call_arguments.length; i++) {
        
        if (i == 3) {
          querystring += encodeURIComponent(call_arguments[i]);
        
        } else {
          querystring += '&' + encodeURIComponent(call_arguments[i]);
        }
      }
    
      // build header
      if (config['transfer_mode'] == 'GET') {
        url = url + querystring;
      } 
    }
  
    // open connection 
    get_connection_object();

    // open connection to remote target
    debug('opening connection to "' + url + '"', 1);
    httpobj.open(config['transfer_mode'], url, config['async']);

    // send "urlencoded" header if necessary (if POST)
    if (config['transfer_mode'] == 'POST') {

      try {
        httpobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

      } catch (cp_err) {
        debug('POST cannot be completed due to incompatible browser.  Use GET as your request method.', 0);
      }
    }

    // make ourselves known
    httpobj.setRequestHeader('X-Powered-By', 'CPAINT v' + version + ' :: http://sf.net/projects/cpaint');

    // callback handling for asynchronous calls
    httpobj.onreadystatechange = callback;

    // send content
    if (config['transfer_mode'] == 'GET') {
      httpobj.send(null);

    } else {
      debug('sending query: ' + querystring, 1);
      httpobj.send(querystring);
    }

    if (config['async'] == true) {
      // manual callback handling for synchronized calls
      callback();
    }
  }
    
  /**
  * internal method for calls to remote servers through the proxy script.
  *
  * @access   public
  * @param    array    call_arguments    array of arguments passed to cpaint.call()
  * @return   void
  */
  this.call_proxy = function(call_arguments) {
    var proxyscript     = config['proxy_url'];
    var url             = call_arguments[0];
    var remote_method   = call_arguments[1];
    var querystring     = '';
    var i               = 0;
    
    var querystring_argument_prefix = 'cpaint_argument[]=';

    // pass parameters to remote method
    if (config['use_cpaint_api'] == false) {
      // when not talking to a CPAINT backend, don't prefix arguments
      querystring_argument_prefix = '';
    }

    for (i = 3; i < call_arguments.length; i++) {

      if (config['use_cpaint_api'] == true) {
      
        if ((typeof call_arguments[i] == 'string'
              && call_arguments[i] != ''
              && call_arguments[i].search(/^\s+$/g) == -1)
          && !isNaN(call_arguments[i])
          && isFinite(call_arguments[i])) {
          // numerical value, convert it first
          querystring += encodeURIComponent(querystring_argument_prefix + JSONCP.stringify(Number(call_arguments[i])) + '&');

        } else {
          querystring += encodeURIComponent(querystring_argument_prefix + JSONCP.stringify(call_arguments[i]) + '&');
        }
        
      } else {
        // no CPAINT in the backend
        querystring += encodeURIComponent(querystring_argument_prefix + call_arguments[i] + '&');
      }
    }

    if (config['use_cpaint_api'] == true) {
      // add remote function name to querystring
      querystring += encodeURIComponent('&cpaint_function=' + remote_method);
  
      // add response type to querystring
      querystring += encodeURIComponent('&cpaint_responsetype=' + config['response_type']);
    }
    
    // build header
    if (config['transfer_mode'] == 'GET') {
      proxyscript += '?cpaint_remote_url=' + encodeURIComponent(url) 
        + '&cpaint_remote_query=' + querystring
        + '&cpaint_remote_method=' + config['transfer_mode'] 
        + '&cpaint_response_type=' + config['response_type'];

    } else {
      querystring = 'cpaint_remote_url=' + encodeURIComponent(url)
        + '&cpaint_remote_query=' + querystring
        + '&cpaint_remote_method=' + config['transfer_mode'] 
        + '&cpaint_response_type=' + config['response_type'];
    }

    // open connection
    get_connection_object();

    // open connection to remote target
    debug('opening connection to proxy "' + proxyscript + '"', 1);
    httpobj.open(config['transfer_mode'], proxyscript, config['async']);

    // send "urlencoded" header if necessary (if POST)
    if (config['transfer_mode'] == 'POST') {

      try {
        httpobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

      } catch (cp_err) {
        debug('POST cannot be completed due to incompatible browser.  Use GET as your request method.', 0);
      }
    }

    httpobj.setRequestHeader('X-Powered-By', 'CPAINT v' + version);

    // callback handling for asynchronous calls
    httpobj.onreadystatechange = callback;

    // send content
    if (config['transfer_mode'] == 'GET') {
      httpobj.send(null);

    } else {
      debug('sending query: ' + querystring, 1);
      httpobj.send(querystring);
    }

    if (config['async'] == false) {
      // manual callback handling for synchronized calls
      callback();
    }
  }

  this.test_ajax_capability = function() {
    return get_connection_object();
  }
  
  /**
  * creates a new connection object.
  *
  * @access   protected
  * @return   boolean
  */
  var get_connection_object = function() {
    var return_value    = false;
    var new_connection  = false;

    // open new connection only if necessary
    if (config['persistent_connection'] == false) {
      // no persistance, create a new object every time
      debug('Using new connection object', 1);
      new_connection = true;

    } else {
      // persistent connection object, only open one if no object exists
      debug('Using shared connection object.', 1);

      if (typeof httpobj != 'object') {
        debug('Getting new persistent connection object.', 1);
        new_connection = true;
      }
    }

    if (new_connection == true) {
      try {
        httpobj = new ActiveXObject('Msxml2.XMLHTTP');
  
      } catch (e) {
        
        try {  
          httpobj = new ActiveXObject('Microsoft.XMLHTTP');
  
        } catch (oc) {
          httpobj = null;
        } 
      }
  
      if (!httpobj && typeof XMLHttpRequest != 'undefined') {
        httpobj = new XMLHttpRequest();
      }
  
      if (!httpobj) {
        debug('Could not create connection object', 0);
      
      } else {
        return_value = true;
      }
    }

    if (httpobj.readyState != 4) {
      httpobj.abort();
    }

    return return_value;
  }

  /**
  * internal callback function.
  *
  * will perform some consistency checks (response code, NULL value testing)
  * and if response_type = 'OBJECT' it will automatically call
  * cpaint_call.parse_ajax_xml() to have a JavaScript object structure generated.
  *
  * after all that is done the client side callback function will be called 
  * with the generated response as single value.
  *
  * @access   protected
  * @return   void
  */
  var callback = function() {
    var response = null;

    if (httpobj.readyState == 4
      && httpobj.status == 200) {
      
      debug(httpobj.responseText, 1);
      debug('using response type ' + config['response_type'], 2);
      
      // fetch correct response
      switch (config['response_type']) {
        case 'XML':
          debug(httpobj.responseXML, 2);
          response = __cpaint_transformer.xml_conversion(httpobj.responseXML);
          break;
          
        case 'OBJECT':
          response = __cpaint_transformer.object_conversion(httpobj.responseXML);
          break;
        
        case 'TEXT':
          response = __cpaint_transformer.text_conversion(httpobj.responseText);
          break;
          
        case 'E4X':
          response = __cpaint_transformer.e4x_conversion(httpobj.responseText);
          break;
          
        case 'JSON':
          response = __cpaint_transformer.json_conversion(httpobj.responseText);
          break;
          
        default:
          debug('invalid response type \'' + response_type + '\'', 0);
      }
      
      // call client side callback
      if (response != null 
        && typeof client_callback == 'function') {
        client_callback(response, httpobj.responseText);
      }
      
      // remove ourselves from the stack
      remove_from_stack();
    
    } else if (httpobj.readyState == 4
      && httpobj.status != 200) {
      // HTTP error of some kind
      debug('invalid HTTP response code \'' + Number(httpobj.status) + '\'', 0);
    }
  }

  /**
  * removes an entry from the stack
  *
  * @access     protected
  * @return     void
  */
  var remove_from_stack = function() {
    // remove only if everything is okay and we're not configured as persistent connection
    if (typeof stack_id == 'number'
      && __cpaint_stack[stack_id]
      && config['persistent_connection'] == false) {
      
      __cpaint_stack[stack_id] = null;
    }
  }

  /**
  * debug method
  *
  * @access  protected
  * @param   string       message         the message to debug
  * @param   integer      debug_level     debug level at which the message appears
  * @return  void
  */
  var debug  = function(message, debug_level) {
    var prefix = '[CPAINT Debug] ';
    
    if (config['debugging'] < 1) {
      prefix = '[CPAINT Error] ';
    }
    
    if (config['debugging'] >= debug_level) {
      alert(prefix + message);
    }
  }
}

/**
* CPAINT transformation object
*
* @package      CPAINT
* @access       public
* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @author       Paul Sullivan <wiley14@gmail.com>
* @author       Dominique Stender <dstender@st-webdevelopment.de>
*/
function cpaint_transformer() {

  /**
  * will take a XMLHttpObject and generate a JavaScript
  * object structure from it.
  *
  * is internally called by cpaint_call.callback() if necessary.
  * will call cpaint_call.create_object_structure() to create nested object structures.
  *
  * @access   public
  * @param    object    xml_document  a XMLHttpObject
  * @return   object
  */
  this.object_conversion = function(xml_document) {
    var return_value  = new cpaint_result_object();
    var i             = 0;
    var firstNodeName = '';
    
    if (typeof xml_document == 'object'
      && xml_document != null) {

      // find the first element node - for MSIE the <?xml?> node is the very first...
      for (i = 0; i < xml_document.childNodes.length; i++) {

        if (xml_document.childNodes[i].nodeType == 1) {
          firstNodeName = xml_document.childNodes[i].nodeName;
          break;
        }
      }
      
      var ajax_response = xml_document.getElementsByTagName(firstNodeName);

      return_value[firstNodeName] = new Array();
    
      for (i = 0; i < ajax_response.length; i++) {
        var tmp_node = create_object_structure(ajax_response[i]);
        tmp_node.id  = ajax_response[i].getAttribute('id')
        return_value[firstNodeName].push(tmp_node);
      }

    } else {
      debug('received invalid XML response', 0);
    }

    return return_value;
  }

  /**
  * performs the necessary conversions for the XML response type
  *
  * @access   public
  * @param    object    xml_document  a XMLHttpObject
  * @return   object
  */
  this.xml_conversion = function(xml_document) {
    return xml_document;
  }
  
  /**
  * performs the necessary conversions for the TEXT response type
  *
  * @access   public
  * @param    string    text  the response text
  * @return   string
  */
  this.text_conversion = function(text) {
    return decode(text);
  }
  
  /**
  * performs the necessary conversions for the E4X response type
  *
  * @access   public
  * @param    string    text  the response text
  * @return   string
  */
  this.e4x_conversion = function(text) {
    // remove <?xml ?>tag
    text = text.replace(/^\<\?xml[^>]+\>/, '');
    return new XML(text);
  }
  
  /**
  * performs the necessary conversions for the JSON response type
  *
  * @access   public
  * @param    string    text  the response text
  * @return   string
  */
  this.json_conversion = function(text) {
    return JSONCP.parse(text);
  }
  
  /**
  * this method takes a HTML / XML node object and creates a
  * JavaScript object structure from it.
  *
  * @access   public
  * @param    object    stream    a node in the XML structure
  * @return   object
  */
  var create_object_structure = function(stream) {
    var return_value = new cpaint_result_object();
    var node_name = '';
    var i         = 0;
    var attrib    = 0;
    
    if (stream.hasChildNodes() == true) {
      for (i = 0; i < stream.childNodes.length; i++) {
  
        node_name = stream.childNodes[i].nodeName;
        node_name = node_name.replace(/[^a-zA-Z0-9_]*/g, '');
        
        // reset / create subnode
        if (typeof return_value[node_name] != 'object') {
          return_value[node_name] = new Array();
        }
        
        if (stream.childNodes[i].nodeType == 1) {
          var tmp_node  = create_object_structure(stream.childNodes[i]);

          for (attrib = 0; attrib < stream.childNodes[i].attributes.length; attrib++) {
            tmp_node.set_attribute(stream.childNodes[i].attributes[attrib].nodeName, stream.childNodes[i].attributes[attrib].nodeValue);
          }
          
          return_value[node_name].push(tmp_node);
        
        } else if (stream.childNodes[i].nodeType == 3) {
          return_value.data  = decode(String(stream.firstChild.data));
        }
      }
    }
    
    return return_value;
  }

  /**
  * converts an encoded text back to viewable characters.
  *
  * @access     public
  * @param      string      rawtext     raw text as provided by the backend
  * @return     mixed
  */
  var decode = function(rawtext) {
    var plaintext = ''; 
    var i         = 0; 
    var c1        = 0;
    var c2        = 0;
    var c3        = 0;
    var u         = 0;
    var t         = 0;

    // remove special JavaScript encoded non-printable characters
    while (i < rawtext.length) {
      if (rawtext.charAt(i) == '\\'
        && rawtext.charAt(i + 1) == 'u') {
        
        u = 0;
        
        for (j = 2; j < 6; j += 1) {
          t = parseInt(rawtext.charAt(i + j), 16);
          
          if (!isFinite(t)) {
            break;
          }
          u = u * 16 + t;
        }

        plaintext += String.fromCharCode(u);
        i       += 6;
      
      } else {
        plaintext += rawtext.charAt(i);
        i++;
      }
    }

    // convert numeric data to number type
    if (plaintext != ''
      && plaintext.search(/^\s+$/g) == -1
      && !isNaN(plaintext) 
      && isFinite(plaintext)) {
      
      plaintext = Number(plaintext);
    }
  
    return plaintext;
  }
}

/**
* this is the basic prototype for a cpaint node object
* as used in cpaint_call.parse_ajax_xml()
*
* @package      CPAINT
* @access       public
* @copyright    Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @author       Paul Sullivan <wiley14@gmail.com>
* @author       Dominique Stender <dstender@st-webdevelopment.de>
*/
function cpaint_result_object() {
  this.id           = 0;
  this.data         = '';
  var __attributes  = new Array();
  
  /**
  * Returns a subnode with the given type and id.
  *
  * @access     public
  * @param      string    type    The type of the subnode. Equivalent to the XML tag name.
  * @param      string    id      The id of the subnode. Equivalent to the XML tag names id attribute.
  * @return     object
  */
  this.find_item_by_id = function() {
    var return_value  = null;
    var type    = arguments[0];
    var id      = arguments[1];
    var i       = 0;
    
    if (this[type]) {

      for (i = 0; i < this[type].length; i++) {

        if (this[type][i].get_attribute('id') == id) {
          return_value = this[type][i];
          break;
        }
      }
    }

    return return_value;
  }
  
  /**
  * retrieves the value of an attribute.
  *
  * @access   public
  * @param    string    name    name of the attribute
  * @return   mixed
  */
  this.get_attribute = function() {
    var return_value  = null;
    var id            = arguments[0];
    
    if (typeof __attributes[id] != 'undefined') {
      return_value = __attributes[id];
    }
    
    return return_value;
  }
  
  /**
  * assigns a value to an attribute.
  *
  * if that attribute does not exist it will be created.
  *
  * @access     public
  * @param      string    name    name of the attribute
  * @param      string    value   value of the attribute
  * @return     void
  */
  this.set_attribute = function() {
    __attributes[arguments[0]] = arguments[1];
  }
}


/*
Copyright (c) 2005 JSON.org

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 Software shall be used for Good, not Evil.

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.
*/

Array.prototype.______array = '______array';

var JSONCP = {
  org: 'http://www.JSON.org',
  copyright: '(c)2005 JSON.org',
  license: 'http://www.crockford.com/JSON/license.html',

  stringify: function (arg) {
    var c, i, l, s = '', v;
    var numeric = true;
    
    switch (typeof arg) {
    case 'object':
      if (arg) {
        if (arg.______array == '______array') {
          // do a test whether all array keys are numeric
          for (i in arg) {
            if (i != '______array'
              && (isNaN(i) 
                || !isFinite(i))) {
              numeric = false;
              break;
            }
          }
          
          if (numeric == true) {
            for (i = 0; i < arg.length; ++i) {
              if (typeof arg[i] != 'undefined') {
                v = this.stringify(arg[i]);
                if (s) {
                  s += ',';
                }
                s += v;
              } else {
                s += ',null';
              }
            }
            return '[' + s + ']';
          } else {
            for (i in arg) {
              if (i != '______array') {
                v = arg[i];
                if (typeof v != 'undefined' && typeof v != 'function') {
                  v = this.stringify(v);
                  if (s) {
                    s += ',';
                  }
                  s += this.stringify(i) + ':' + v;
                }
              }
            }
            // return as object
            return '{' + s + '}';
          }
        } else if (typeof arg.toString != 'undefined') {
          for (i in arg) {
            v = arg[i];
            if (typeof v != 'undefined' && typeof v != 'function') {
              v = this.stringify(v);
              if (s) {
                s += ',';
              }
              s += this.stringify(i) + ':' + v;
            }
          }
          return '{' + s + '}';
        }
      }
      return 'null';
    case 'number':
      return isFinite(arg) ? String(arg) : 'null';
    case 'string':
      l = arg.length;
      s = '"';
      for (i = 0; i < l; i += 1) {
        c = arg.charAt(i);
        if (c >= ' ') {
          if (c == '\\' || c == '"') {
            s += '\\';
          }
          s += c;
        } else {
          switch (c) {
            case '\b':
              s += '\\b';
              break;
            case '\f':
              s += '\\f';
              break;
            case '\n':
              s += '\\n';
              break;
            case '\r':
              s += '\\r';
              break;
            case '\t':
              s += '\\t';
              break;
            default:
              c = c.charCodeAt();
              s += '\\u00' + Math.floor(c / 16).toString(16) +
                (c % 16).toString(16);
          }
        }
      }
      return s + '"';
    case 'boolean':
      return String(arg);
    default:
      return 'null';
    }
  },
  parse: function (text) {
    var at = 0;
    var ch = ' ';

    function error(m) {
      throw {
        name: 'JSONError',
        message: m,
        at: at - 1,
        text: text
      };
    }

    function next() {
      ch = text.charAt(at);
      at += 1;
      return ch;
    }

    function white() {
      while (ch != '' && ch <= ' ') {
        next();
      }
    }

    function str() {
      var i, s = '', t, u;

      if (ch == '"') {
outer:      while (next()) {
          if (ch == '"') {
            next();
            return s;
          } else if (ch == '\\') {
            switch (next()) {
            case 'b':
              s += '\b';
              break;
            case 'f':
              s += '\f';
              break;
            case 'n':
              s += '\n';
              break;
            case 'r':
              s += '\r';
              break;
            case 't':
              s += '\t';
              break;
            case 'u':
              u = 0;
              for (i = 0; i < 4; i += 1) {
                t = parseInt(next(), 16);
                if (!isFinite(t)) {
                  break outer;
                }
                u = u * 16 + t;
              }
              s += String.fromCharCode(u);
              break;
            default:
              s += ch;
            }
          } else {
            s += ch;
          }
        }
      }
      error("Bad string");
    }

    function arr() {
      var a = [];

      if (ch == '[') {
        next();
        white();
        if (ch == ']') {
          next();
          return a;
        }
        while (ch) {
          a.push(val());
          white();
          if (ch == ']') {
            next();
            return a;
          } else if (ch != ',') {
            break;
          }
          next();
          white();
        }
      }
      error("Bad array");
    }

    function obj() {
      var k, o = {};

      if (ch == '{') {
        next();
        white();
        if (ch == '}') {
          next();
          return o;
        }
        while (ch) {
          k = str();
          white();
          if (ch != ':') {
            break;
          }
          next();
          o[k] = val();
          white();
          if (ch == '}') {
            next();
            return o;
          } else if (ch != ',') {
            break;
          }
          next();
          white();
        }
      }
      error("Bad object");
    }

    function assoc() {
      var k, a = [];

      if (ch == '<') {
        next();
        white();
        if (ch == '>') {
          next();
          return a;
        }
        while (ch) {
          k = str();
          white();
          if (ch != ':') {
            break;
          }
          next();
          a[k] = val();
          white();
          if (ch == '>') {
            next();
            return a;
          } else if (ch != ',') {
            break;
          }
          next();
          white();
        }
      }
      error("Bad associative array");
    }

    function num() {
      var n = '', v;
      if (ch == '-') {
        n = '-';
        next();
      }
      while (ch >= '0' && ch <= '9') {
        n += ch;
        next();
      }
      if (ch == '.') {
        n += '.';
        while (next() && ch >= '0' && ch <= '9') {
          n += ch;
        }
      }
      if (ch == 'e' || ch == 'E') {
        n += 'e';
        next();
        if (ch == '-' || ch == '+') {
          n += ch;
          next();
        }
        while (ch >= '0' && ch <= '9') {
          n += ch;
          next();
        }
      }
      v = +n;
      if (!isFinite(v)) {
        error("Bad number");
      } else {
        return v;
      }
    }

    function word() {
      switch (ch) {
        case 't':
          if (next() == 'r' && next() == 'u' && next() == 'e') {
            next();
            return true;
          }
          break;
        case 'f':
          if (next() == 'a' && next() == 'l' && next() == 's' &&
              next() == 'e') {
            next();
            return false;
          }
          break;
        case 'n':
          if (next() == 'u' && next() == 'l' && next() == 'l') {
            next();
            return null;
          }
          break;
      }
      error("Syntax error");
    }

    function val() {
      white();
      switch (ch) {
        case '{':
          return obj();
        case '[':
          return arr();
        case '<':
          return assoc();
        case '"':
          return str();
        case '-':
          return num();
        default:
          return ch >= '0' && ch <= '9' ? num() : word();
      }
    }

    return val();
  }
};


/*
 * ActiveSpell
 * Copyright (C) 2006 ActiveCampaign, Inc.
 *
 * Licensed under the terms of the GNU Lesser General Public License:
 * 		http://www.opensource.org/licenses/lgpl-license.php
 * 
 * About:
 *		General Information: http://www.activecampaign.com/activespell
 * 		Forum: http://www.activecampaign.com/support/forum/forumdisplay.php?f=40
 *		ActiveSpell 1.0 was based on ajax-spell (http://www.broken-notebook.com/spell_checker)
 */
 
var cp = new cpaint();
cp.set_transfer_mode('post');
cp.set_response_type('text');
//cp.set_debug(1);

var currObj; //the current spell checker being used
var spellingSuggestionsDiv = null;  // Auto-generated suggestions div

/*
 * Cross-browser attempt at supporting the regular function,
 * document.getElementById().  That function is a DOM-2 level function,
 * which is something that IE -- even at version 6 -- does not yet fully
 * support.
 */

function get_id(id) {
    if (document.getElementById)
        return document.getElementById(id);
    else
        return document.all[id];        /* probably IE */
}

// If there are already any onclick handlers loaded in the page, we'll add
// our onclick handler first and then call the old one, rather than completely
// overriding it.  The checkClickLocation is used to hide the suggestions div
// when the user clicks outside it.



if(document.onclick)
{
    
	var old_onclick = document.onclick;
	document.onclick = function(e)
	{        
		checkClickLocation(e);
		old_onclick(e);
	}
    
}
else
{
	document.onclick = checkClickLocation;
}


/* MF2HD - ebava se s highslide */
/*
    var old_onclick = document.onclick;
    document.onclick = function(e)
    {        
        checkClickLocation(e);
        if (old_onclick) {
        old_onclick(e);
        }
    }
*/
// If there are already any onload handlers loaded in the page, we'll add our onload
// handler first and then call the old one, rather than completely overriding it.
/*
if(window.onload)
{
	var old_onload = window.onload;
	window.onload = function(e)
	{
		setupSpellCheckers(e);
		old_onload(e);
	}
}
else
{
	window.onload = setupSpellCheckers;
}
*/


/*************************************************************
 * function setupSpellCheckers()
 *
 * This function goes through the page and finds all the 
 * textareas.  It then checks the title attribute for either
 * spellcheck or spellcheck_icons to determine whether or not
 * it should add a spellchecker to that textarea.
 *************************************************************/
function setupSpellCheckers()
{

	var textareas = document.getElementsByTagName('textarea');
	var numSpellCheckers = 0;
	var tempSpellCheckers = Array();

	for(var i=0; i < textareas.length; i++)
	{
		if(textareas[i].getAttribute("title") == "spellcheck" || textareas[i].getAttribute("title") == "spellcheck_icons")
		{
			tempSpellCheckers[numSpellCheckers] = textareas[i];
			
			//create a new spellchecker for this textarea
			var tempWidth = tempSpellCheckers[numSpellCheckers].offsetWidth + 'px';
			var tempHeight = tempSpellCheckers[numSpellCheckers].offsetHeight + 'px';
			eval('spellCheckers' + numSpellCheckers + '= new activeSpell("spellCheckers' + numSpellCheckers + '", tempWidth, tempHeight, tempSpellCheckers[' + numSpellCheckers + '].getAttribute("accesskey"), "spellCheckDiv' + numSpellCheckers + '", tempSpellCheckers[' + numSpellCheckers + '].getAttribute("name"), tempSpellCheckers[' + numSpellCheckers + '].id, tempSpellCheckers[' + numSpellCheckers + '].title, tempSpellCheckers[' + numSpellCheckers + '].value);');
			
			numSpellCheckers++;
		}
	}
	
}; // end setInit


/*************************************************************
 * activeSpell(varName, width, height, spellUrl, divId, name, id)
 *
 * This is the constructor that creates a new activeSpell object.
 * All of it is dynamically generated so the user doesn't have
 * to add a bunch of crap to their site.
 *
 * @param varName The name of the variable that the object is
 *                assigned to (must be unique and the same as the variable)
 * @param width The width of the spell checker
 * @param height The height of the spell checker
 * @param spellUrl The url of the spell_checker.php code
 * @param divId The id of the div that the spell checker is 
 *              contained in (must be unique)
 * @param name The name of the textarea form element
 * @param id The id of the spell checker textarea (must be unique)
 *************************************************************/
function activeSpell(varName, width, height, spellUrl, divId, name, id, title, value)
{
	currObj = this;

	currObj.config               = new Array();         //the array of configuration options
	currObj.config['varName']    = varName;             //the name of the variable that this instance is stored in
	currObj.config['width']      = width;               //the width of the textarea
	currObj.config['height']     = height;              //the height of the textarea
	currObj.config['spellUrl']   = spellUrl;            //url to spell checker php code (spell_checker.php by default);
	currObj.config['divId']      = divId;               //the id of the div that the spell checker element is in
	currObj.config['name']       = name;                //what you want the form element's name to be
	currObj.config['id']         = id;                  //the unique id of the spell_checker textarea
	currObj.config['title']      = title;               //the title (specifies whether to use icons or not);
	currObj.config['value']      = value;               //the value of the text box when the page was loaded

	currObj.config['imagePath'] = spellUrl.substring(0, spellUrl.lastIndexOf("/")+1);
	
	currObj.config['value']      = currObj.config['value'].replace(/<br *\/?>/gi, "\n");
	
	currObj.config['useIcons'] = false;
	
	if(currObj.config['title'] == "spellcheck_icons")
	{
		currObj.config['useIcons'] = true;
	}
	
	spellContainer = document.createElement('DIV');
	spellContainer.id = currObj.config['divId'];
	spellContainer.className = 'spell_container';
	spellContainer.style.width = currObj.config['width'];

	oldElement = document.getElementById(currObj.config['id']);

	oldElement.parentNode.replaceChild(spellContainer, oldElement);
	
	//generate the div to hold the spell checker controls
	currObj.controlPanelDiv = document.createElement('DIV');
	currObj.controlPanelDiv.className = 'control_panel';
	//document.getElementById(currObj.config['divId']).appendChild(currObj.controlPanelDiv);
	
	//the span that toggles between spell checking and editing
	currObj.actionSpan = document.createElement('SPAN');
	currObj.actionSpan.className = "action";
	currObj.actionSpan.id = "action";
	if(currObj.config['useIcons'])
	{
		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\"><img src=\"" + currObj.config['imagePath'] + "images/spellcheck.png\" width=\"16\" height=\"16\" title=\""+JS_CHECK_SPELLING_AND_PREVIEW+"\" alt=\""+JS_CHECK_SPELLING_AND_PREVIEW+"\" border=\"0\" /></a>";
	}
	else
	{
		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\">"+JS_CHECK_SPELLING+"</a>";
	}
	currObj.controlPanelDiv.appendChild(currObj.actionSpan);
	
	//the span that lets the user know of the status of the spell checker
	currObj.statusSpan = document.createElement('SPAN');
	currObj.statusSpan.className = "status";
	currObj.statusSpan.id = "status";
	currObj.statusSpan.innerHTML = "";
	currObj.controlPanelDiv.appendChild(currObj.statusSpan);
	
	//the textarea to be spell checked
	oldElement.value = currObj.config['value'];
	document.getElementById(currObj.config['divId']).appendChild(oldElement);
	
    document.getElementById(currObj.config['divId']).appendChild(currObj.controlPanelDiv);
    
	currObj.objToCheck              = document.getElementById(currObj.config['id']);      //the actual object we're spell checking
	currObj.spellingResultsDiv      = null;                                               // Auto-generated results div
		
	//prototypes for the activeSpell objects
	activeSpell.prototype.spellCheck           = spellCheck;
	activeSpell.prototype.spellCheck_cb        = spellCheck_cb;
	activeSpell.prototype.showSuggestions      = showSuggestions;
	activeSpell.prototype.showSuggestions_cb   = showSuggestions_cb;
	activeSpell.prototype.replaceWord          = replaceWord;
	activeSpell.prototype.switchText           = switchText;
	activeSpell.prototype.switchText_cb        = switchText_cb;
	activeSpell.prototype.resumeEditing        = resumeEditing;
	activeSpell.prototype.resetSpellChecker    = resetSpellChecker;
	activeSpell.prototype.resetAction          = resetAction;
}; // end activeSpell


/*************************************************************
 * setCurrentObject
 *
 * This sets the current object to be the spell checker that
 * the user is currently using.
 *
 * @param obj The spell checker currently being used
 *************************************************************/
function setCurrentObject(obj)
{
	currObj = obj;
}; // end setCurrentObject


/*************************************************************
 * spellCheck_cb
 *
 * This is the callback function that the spellCheck php function
 * returns the spell checked data to.  It sets the results div
 * to contain the markedup misspelled data and changes the status
 * message.  It also sets the width and height of the results
 * div to match the element that's being checked.
 * If there are no misspellings then new_data is the empty 
 * string and the status is set to "No Misspellings Found".
 *
 * @param new_data The marked up misspelled data returned from php.
 *************************************************************/
function spellCheck_cb(new_data)
{
	with(currObj);
	new_data = new_data.toString();
	var isThereAMisspelling = new_data.charAt(0);
	new_data = new_data.substring(1);
		
	if(currObj.spellingResultsDiv)
	{
		currObj.spellingResultsDiv.parentNode.removeChild(spellingResultsDiv);
	}
	
	currObj.spellingResultsDiv = document.createElement('DIV');
	currObj.spellingResultsDiv.className = 'edit_box';
	currObj.spellingResultsDiv.style.width = currObj.objToCheck.style.width;
	currObj.spellingResultsDiv.style.height = currObj.objToCheck.style.height;
	currObj.spellingResultsDiv.innerHTML = new_data;
	
	currObj.objToCheck.style.display = "none";
	currObj.objToCheck.parentNode.insertBefore(currObj.spellingResultsDiv,currObj.objToCheck);
	currObj.statusSpan.innerHTML = "";
	
	if(currObj.config['useIcons'])
	{
		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".resumeEditing();\"><img src=\"" + currObj.config['imagePath'] + "images/page_white_edit.png\" width=\"16\" height=\"16\" title=\""+JS_RESUME_EDITING+"\" alt=\""+JS_RESUME_EDITING+"\" border=\"0\" /></a>";
	}
	else
	{
		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".resumeEditing();\">"+JS_RESUME_EDITING+"</a>";
	}
		
	if(isThereAMisspelling != "1")
	{
		if(currObj.config['useIcons'])
		{
			currObj.statusSpan.innerHTML = "<img src=\"" + currObj.config['imagePath'] + "images/accept.png\" width=\"16\" height=\"16\" title=\""+JS_NO_MISSSPELL_FOUND+"\" alt=\""+JS_NO_MISSSPELL_FOUND+"\" border=\"0\" />";
		}
		else
		{
			currObj.statusSpan.innerHTML = JS_NO_MISSSPELL_FOUND;
		}
		currObj.objToCheck.disabled = false;
	}
	
}; // end spellCheck_cb


/*************************************************************
 * spellCheck()
 *
 * The spellCheck javascript function sends the text entered by
 * the user in the text box to php to be spell checked.  It also
 * sets the status message to "Checking..." because it's currently
 * checking the spelling.
 *************************************************************/
function spellCheck() {
	with(currObj);
	var query;
	
	if(currObj.spellingResultsDiv)
	{
		currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv);
		currObj.spellingResultsDiv = null;
	}
	
	if(currObj.config['useIcons'])
	{
		currObj.actionSpan.innerHTML = "<img src=\"" + currObj.config['imagePath'] + "images/spellcheck.png\" width=\"16\" height=\"16\" title=\""+JS_CHECK_SPELLING_AND_PREVIEW+"\" alt=\""+JS_CHECK_SPELLING_AND_PREVIEW+"\" border=\"0\" />";
	}
	else
	{
		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\">"+JS_CHECK_SPELLING_AND_PREVIEW+"</a>";
	}
	
	if(currObj.config['useIcons'])
	{
		currObj.statusSpan.innerHTML = "<img src=\"" + currObj.config['imagePath'] + "images/working.gif\" width=\"16\" height=\"16\" title=\""+JS_CHECKING+"\" alt=\""+JS_CHECKING+"\" border=\"0\" />";
	}
	else
	{
		currObj.statusSpan.innerHTML = JS_CHECKING;
	}
	query = currObj.objToCheck.value;
	query = query.replace(/\r?\n/gi, "<br />");
	
	cp.call(currObj.config['spellUrl'], 'spellCheck', spellCheck_cb, query, currObj.config['varName']);

}; // end spellcheck



/*************************************************************
 * addWord
 *
 * The addWord function adds a word to the custom dictionary
 * file.
 *
 * @param id The id of the span that contains the word to be added
 *************************************************************/
function addWord(id)
{
	var wordToAdd = document.getElementById(id).innerHTML;
	
	with(currObj);
	
	if(spellingSuggestionsDiv)
	{
		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
		spellingSuggestionsDiv = null;
	}
	
	if(currObj.config['useIcons'])
	{
		currObj.statusSpan.innerHTML = "<img src=\"" + currObj.config['imagePath'] + "images/working.gif\" width=\"16\" height=\"16\" title=\""+JS_ADDING_WORD+"\" alt=\""+JS_ADDING_WORD+"\" border=\"0\" />";
	}
	else
	{
		currObj.statusSpan.innerHTML = JS_ADDING_WORD;
	}

	cp.call(currObj.config['spellUrl'], 'addWord', addWord_cb, wordToAdd);

}; // end addWord

/*************************************************************
 * addWord_cb
 *
 * The addWord_cb function is a callback function that
 * PHP's addWord function returns to.  It recieves the
 * return status of the add to word to personal dictionary call.
 * It hides the status item.
 *
 * @param returnedData The return code from PHP.
 *************************************************************/
function addWord_cb(returnedData)
{
	//alert(returnedData);
	with(currObj);
	currObj.statusSpan.innerHTML = "";
	resumeEditing();
	spellCheck();
}; // end addWord_cb



/*************************************************************
 * checkClickLocation(e)
 *
 * This function is called by the event listener when the user
 * clicks on anything.  It is used to close the suggestion div
 * if the user clicks anywhere that's not inside the suggestion
 * div.  It just checks to see if the name of what the user clicks
 * on is not "suggestions" then hides the div if it's not.
 *
 * @param e The event, in this case the user clicking somewhere on
 *          the page.
 *************************************************************/
function checkClickLocation(e)
{
	if(spellingSuggestionsDiv)
	{
		// Bah.  There's got to be a better way to deal with this, but the click
		// on a word to get suggestions starts up a race condition between
		// showing and hiding the suggestion box, so we'll ignore the first
		// click.
		if(spellingSuggestionsDiv.ignoreNextClick){
			spellingSuggestionsDiv.ignoreNextClick = false;
		}
		else
		{
			var theTarget = getTarget(e);
			
			if(theTarget != spellingSuggestionsDiv)
			{
				spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
				spellingSuggestionsDiv = null;
			}
		}
	}
	
	return true; // Allow other handlers to continue.
}; //end checkClickLocation


/*************************************************************
 * getTarget
 *
 * The get target function gets the correct target of the event.
 * This function is required because IE handles the events in
 * a different (wrong) manner than the rest of the browsers.
 *
 * @param e The target, in this case the user clicking somewhere on
 *     the page.
 *
 *************************************************************/
function getTarget(e)
{
	var value;
	if(checkBrowser() == "ie")
	{
		value = window.event.srcElement;
	}
	else
	{
		value = e.target;
	}
	return value;
}; //end getTarget


/*************************************************************
 * checkBrowser()
 *
 * The checkBrowser function simply checks to see what browser
 * the user is using and returns a string containing the browser
 * type.
 *
 * @return string The browser type
 *************************************************************/
function checkBrowser()
{
	var theAgent = navigator.userAgent.toLowerCase();
	if(theAgent.indexOf("msie") != -1)
	{
		if(theAgent.indexOf("opera") != -1)
		{
			return "opera";
		}
		else
		{
			return "ie";
		}
	}
	else if(theAgent.indexOf("netscape") != -1)
	{
		return "netscape";
	}
	else if(theAgent.indexOf("firefox") != -1)
	{
		return "firefox";
	}
	else if(theAgent.indexOf("mozilla/5.0") != -1)
	{
		return "mozilla";
	}
	else if(theAgent.indexOf("\/") != -1)
	{
		if(theAgent.substr(0,theAgent.indexOf('\/')) != 'mozilla')
		{
			return navigator.userAgent.substr(0,theAgent.indexOf('\/'));
		}
		else
		{
			return "netscape";
		} 
	}
	else if(theAgent.indexOf(' ') != -1)
	{
		return navigator.userAgent.substr(0,theAgent.indexOf(' '));
	}
	else
	{ 
		return navigator.userAgent;
	}
}; // end checkBrowser


/*************************************************************
 * showSuggestions_cb
 *
 * The showSuggestions_cb function is a callback function that
 * php's showSuggestions function returns to.  It sets the 
 * suggestions table to contain the new data and then displays
 * the suggestions div.  It also clears the status message.
 *
 * @param new_data The suggestions table returned from php.
 *************************************************************/
function showSuggestions_cb(new_data)
{
	with(currObj);
	spellingSuggestionsDiv.innerHTML = new_data;
	spellingSuggestionsDiv.style.display = 'block';
	currObj.statusSpan.innerHTML = "";    
}; //end showSuggestions_cb


/*************************************************************
 * showSuggestions
 *
 * The showSuggestions function calls the showSuggestions php
 * function to get suggestions for the misspelled word that the
 * user has clicked on.  It sets the status to "Searching...",
 * hides the suggestions div, finds the x and y position of the
 * span containing the misspelled word that user clicked on so 
 * the div can be displayed in the correct location, and then
 * calls the showSuggestions php function with the misspelled word
 * and the id of the span containing it.
 *
 * @param word The misspelled word that the user clicked on
 * @param id The id of the span that contains the misspelled word
 *************************************************************/
function showSuggestions(word, id)
{
	with(currObj);
	if(currObj.config['useIcons'])
	{
		currObj.statusSpan.innerHTML = "<img src=\"" + currObj.config['imagePath'] + "images/working.gif\" width=\"16\" height=\"16\" title=\""+JS_SEARCHING+"\" alt=\""+JS_SEARCHING+"\" border=\"0\" />";
	}
	else
	{
		currObj.statusSpan.innerHTML = JS_SEARCHING;
	}
	var x = findPosXById(id);
	var y = findPosYById(id);
	
	var scrollPos = 0;
	if(checkBrowser() != "ie")
	{
		scrollPos = currObj.spellingResultsDiv.scrollTop;
	}

	if(spellingSuggestionsDiv)
	{
		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
	}
	spellingSuggestionsDiv = document.createElement('DIV');
	spellingSuggestionsDiv.style.display = "none";
	spellingSuggestionsDiv.className = 'suggestion_box';
	spellingSuggestionsDiv.style.position = 'absolute';
	spellingSuggestionsDiv.style.left = x + 'px';
	spellingSuggestionsDiv.style.top = (y+16-scrollPos) + 'px';

	// Bah. There's got to be a better way to deal with this, but the click
	// on a word to get suggestions starts up a race condition between
	// showing and hiding the suggestion box, so we'll ignore the first
	// click.
	spellingSuggestionsDiv.ignoreNextClick = true;
	
	document.body.appendChild(spellingSuggestionsDiv);

	cp.call(currObj.config['spellUrl'], 'showSuggestions', showSuggestions_cb, word, id);
}; // end showSuggestions


/*************************************************************
 * replaceWord
 *
 * The replaceWord function takes the id of the misspelled word
 * that the user clicked on and replaces the innerHTML of that
 * span with the new word that the user selects from the suggestion
 * div.  It hides the suggestions div and changes the color of
 * the previously misspelled word to green to let the user know
 * it has been changed.  It then calls the switchText php function
 * with the innerHTML of the div to update the text of the text box.
 *
 * @param id The id of the span that contains the word to be replaced
 * @param newWord The word the user selected from the suggestions div
 *                to replace the misspelled word.
 *************************************************************/
function replaceWord(id, newWord)
{
	document.getElementById(id).innerHTML = trim(newWord);
	if(spellingSuggestionsDiv)
	{
		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
		spellingSuggestionsDiv = null;
	}
	document.getElementById(id).className = "corrected_word";
}; // end replaceWord


/*************************************************************
 * switchText
 *
 * The switchText function is a funtion is called when the user
 * clicks on resume editing (or submits the form).  It calls the
 * php function to switchText and uncomments the html and replaces
 * breaks and everything.  Here all the breaks that the user has
 * typed are replaced with %u2026.  Firefox does this goofy thing
 * where it cleans up the display of your html, which adds in \n's
 * where you don't want them.  So I replace the user-entered returns
 * with something unique so that I can rip out all the breaks that
 * the browser might add and we don't want.
 *************************************************************/
function switchText()
{
	with(currObj);
	var text = currObj.spellingResultsDiv.innerHTML;
	text = text.replace(/<br *\/?>/gi, "~~~");
	// Work around a cpaint/safari bug by prefixing an asterisk to the text so that the text is never completely empty
	text = '*' + text;

	cp.call(currObj.config['spellUrl'], 'switchText', switchText_cb, text);
}; // end switchText


/*************************************************************
 * switchText_cb
 *
 * The switchText_cb function is a call back funtion that the
 * switchText php function returns to.  I replace all the %u2026's
 * with returns.  It then replaces the text in the text box with 
 * the corrected text fromt he div.
 *
 * @param new_string The corrected text from the div.
 *
 *************************************************************/
function switchText_cb(new_string)
{
	with(currObj);
	new_string = new_string.replace(/~~~/gi, "\n");

	// Remove the prefixed asterisk that was added in switchText().
	new_string = new_string.substr(1);
	currObj.objToCheck.style.display = "none";
	currObj.objToCheck.value = new_string;
	currObj.objToCheck.disabled = false;
	if(currObj.spellingResultsDiv)
	{
		currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv);
		currObj.spellingResultsDiv = null;
	}
	currObj.objToCheck.style.display = "block";
	currObj.resetAction();
}; // end switchText_cb


/*************************************************************
 * resumeEditing
 *
 * The resumeEditing function is called when the user is in the
 * correction mode and wants to return to the editing mode.  It
 * hides the results div and the suggestions div, then enables
 * the text box and unhides the text box.  It also calls
 * resetAction() to reset the status message.
 *************************************************************/
function resumeEditing()
{
	with(currObj);
	if(currObj.config['useIcons'])
	{
		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\"><img src=\"" + currObj.config['imagePath'] + "images/page_white_edit.png\" width=\"16\" height=\"16\" title=\""+JS_RESUME_EDITING+"\" alt=\""+JS_RESUME_EDITING+"\" border=\"0\" /></a>";
	}
	else
	{
		currObj.actionSpan.innerHTML = "<a class=\"resume_editing\">"+JS_RESUME_EDITING+"</a>";
	}
	if(currObj.config['useIcons'])
	{
		currObj.statusSpan.innerHTML = "<img src=\"" + currObj.config['imagePath'] + "images/working.gif\" width=\"16\" height=\"16\" title=\""+JS_WORKING+"\" alt=\""+JS_WORKING+"\" border=\"0\" />";
	}
	else
	{
		currObj.statusSpan.innerHTML = JS_WORKING;
	}
	
	if(spellingSuggestionsDiv)
	{
		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
		spellingSuggestionsDiv = null;
	}
	
	currObj.switchText();
}; // end resumeEditing


/*************************************************************
 * resetAction
 *
 * The resetAction function just resets the status message to
 * the default action of "Check Spelling".
 *************************************************************/
function resetAction()
{
	with(currObj);
	if(currObj.config['useIcons'])
	{
		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\"><img src=\"" + currObj.config['imagePath'] + "images/spellcheck.png\" width=\"16\" height=\"16\" title=\""+JS_CHECK_SPELLING_AND_PREVIEW+"\" alt=\""+JS_CHECK_SPELLING_AND_PREVIEW+"\" border=\"0\" /></a>";
	}
	else
	{
		currObj.actionSpan.innerHTML = "<a class=\"check_spelling\" onclick=\"setCurrentObject(" + currObj.config['varName'] + "); " + currObj.config['varName'] + ".spellCheck();\">"+JS_CHECK_SPELLING_AND_PREVIEW+"</a>";
	}

	currObj.statusSpan.innerHTML = "";
}; // end resetAction


/*************************************************************
 * resetSpellChecker
 *
 * The resetSpellChecker function resets the entire spell checker
 * to the defaults.
 *************************************************************/
function resetSpellChecker()
{
	with(currObj);
	currObj.resetAction();
	
	currObj.objToCheck.value = "";
	currObj.objToCheck.style.display = "block";
	currObj.objToCheck.disabled = false;
	
	if(currObj.spellingResultsDiv)
	{
		currObj.spellingResultsDiv.parentNode.removeChild(currObj.spellingResultsDiv);
		currObj.spellingResultsDiv = null;
	}
	if(spellingSuggestionsDiv)
	{
		spellingSuggestionsDiv.parentNode.removeChild(spellingSuggestionsDiv);
		spellingSuggestionsDiv = null;
	}
	currObj.statusSpan.style.display = "none";
	
}; // end resetSpellChecker


/*************************************************************
 * findPosX
 *
 * The findPosX function just finds the X offset of the top left
 * corner of the object id it's given.
 *
 * @param object The id of the object that you want to find the 
 *               upper left X coordinate of.
 * @return int The X coordinate of the object
 *************************************************************/
function findPosXById(object)
{
	var curleft = 0;
	var obj = document.getElementById(object);
	if(obj.offsetParent)
	{
		while(obj.offsetParent)
		{
			curleft += obj.offsetLeft - obj.scrollLeft;
			obj = obj.offsetParent;
		}
	}
	else if(obj.x)
	{
		curleft += obj.x;
	}
	return curleft;
}; // end findPosX


/*************************************************************
 * findPosY
 *
 * The findPosY function just finds the Y offset of the top left
 * corner of the object id it's given.
 *
 * @param object The id of the object that you want to find the 
 *               upper left Y coordinate of.
 * @return int The Y coordinate of the object
 *************************************************************/
function findPosYById(object)
{
	var curtop = 0;var curtop = 0;
	var obj = document.getElementById(object);
	if(obj.offsetParent)
	{
		while(obj.offsetParent)
		{
			curtop += obj.offsetTop - obj.scrollTop;
			obj = obj.offsetParent;
		}
	}
	else if(obj.y)
	{
		curtop += obj.y;
	}
	return curtop;
}; // end findPosY


/*************************************************************
 * trim
 *
 * Trims white space from a string.
 *
 * @param s The string you want to trim.
 * @return string The trimmed string.
 *************************************************************/
function trim(s)
{
	while(s.substring(0,1) == ' ')
	{
    	s = s.substring(1,s.length);
	}
	while(s.substring(s.length-1,s.length) == ' ')
	{
    	s = s.substring(0,s.length-1);
	}
	return s;
}; // end trim

eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('l(1T 1z.6=="Q"){1z.Q=1z.Q;u 6=q(a,c){l(a&&1T a=="q"&&6.C.1W)v 6(17).1W(a);a=a||6.1o||17;l(a.3E)v 6(6.1X(a,[]));l(c&&c.3E)v 6(c).1V(a);l(1z==7)v 1h 6(a,c);l(a.O==1C){u m=/^[^<]*(<.+>)[^>]*$/.3d(a);l(m)a=6.3D([m[1]])}7.1n(a.O==2z||a.D&&!a.1R&&a[0]!=Q&&a[0].1R?6.1X(a,[]):6.1V(a,c));u C=19[19.D-1];l(C&&1T C=="q")7.W(C);v 7};l(1T $!="Q")6.44$=$;u $=6;6.C=6.8b={3E:"1.0.3",5J:q(){v 7.D},1n:q(23){l(23&&23.O==2z){7.D=0;[].1k.16(7,23);v 7}G v 23==Q?6.1X(7,[]):7[23]},W:q(C,1g){v 6.W(7,C,1g)},8g:q(15){u 2j=-1;7.W(q(i){l(7==15)2j=i});v 2j},1t:q(1L,Y,B){v 1L.O!=1C||Y!=Q?7.W(q(){l(Y==Q)I(u E 1r 1L)6.1t(B?7.1a:7,E,1L[E]);G 6.1t(B?7.1a:7,1L,Y)}):6[B||"1t"](7[0],1L)},1f:q(1L,Y){v 7.1t(1L,Y,"26")},2B:q(e){e=e||7;u t="";I(u j=0;j<e.D;j++){u r=e[j].2f;I(u i=0;i<r.D;i++)l(r[i].1R!=8)t+=r[i].1R!=1?r[i].4Z:6.C.2B([r[i]])}v t},1Y:q(){u a=6.3D(19);v 7.W(q(){u b=a[0].3f(T);7.1i.2Y(b,7);24(b.2a)b=b.2a;b.4e(7)})},5g:q(){v 7.2T(19,T,1,q(a){7.4e(a)})},5h:q(){v 7.2T(19,T,-1,q(a){7.2Y(a,7.2a)})},5i:q(){v 7.2T(19,U,1,q(a){7.1i.2Y(a,7)})},5j:q(){v 7.2T(19,U,-1,q(a){7.1i.2Y(a,7.8j)})},4q:q(){v 7.1n(7.33.8k())},1V:q(t){v 7.2n(6.2r(7,q(a){v 6.1V(t,a)}),19)},4f:q(4D){v 7.2n(6.2r(7,q(a){v a.3f(4D!=Q?4D:T)}),19)},1c:q(t){v 7.2n(t.O==2z&&6.2r(7,q(a){I(u i=0;i<t.D;i++)l(6.1c(t[i],[a]).r.D)v a;v U})||t.O==8l&&(t?7.1n():[])||1T t=="q"&&6.2O(7,t)||6.1c(t,7).r,19)},2t:q(t){v 7.2n(t.O==1C?6.1c(t,7,U).r:6.2O(7,q(a){v a!=t}),19)},2g:q(t){v 7.2n(6.1X(7,t.O==1C?6.1V(t):t.O==2z?t:[t]),19)},4E:q(2u){v 2u?6.1c(2u,7).r.D>0:U},2T:q(1g,22,2X,C){u 4f=7.5J()>1;u a=6.3D(1g);v 7.W(q(){u 15=7;l(22&&7.2p.2b()=="8m"&&a[0].2p.2b()!="62"){u 29=7.4S("29");l(!29.D){15=17.5N("29");7.4e(15)}G 15=29[0]}I(u i=(2X<0?a.D-1:0);i!=(2X<0?2X:a.D);i+=2X){C.16(15,[4f?a[i].3f(T):a[i]])}})},2n:q(a,1g){u C=1g&&1g[1g.D-1];u 2d=1g&&1g[1g.D-2];l(C&&C.O!=1v)C=M;l(2d&&2d.O!=1v)2d=M;l(!C){l(!7.33)7.33=[];7.33.1k(7.1n());7.1n(a)}G{u 1Z=7.1n();7.1n(a);l(2d&&a.D||!2d)7.W(2d||C).1n(1Z);G 7.1n(1Z).W(C)}v 7}};6.1y=6.C.1y=q(15,E){l(19.D>1&&(E===M||E==Q))v 15;l(!E){E=15;15=7}I(u i 1r E)15[i]=E[i];v 15};6.1y({5C:q(){6.65=T;6.W(6.2e.5r,q(i,n){6.C[i]=q(a){u L=6.2r(7,n);l(a&&a.O==1C)L=6.1c(a,L).r;v 7.2n(L,19)}});6.W(6.2e.2o,q(i,n){6.C[i]=q(){u a=19;v 7.W(q(){I(u j=0;j<a.D;j++)6(a[j])[n](7)})}});6.W(6.2e.W,q(i,n){6.C[i]=q(){v 7.W(n,19)}});6.W(6.2e.1c,q(i,n){6.C[n]=q(23,C){v 7.1c(":"+n+"("+23+")",C)}});6.W(6.2e.1t,q(i,n){n=n||i;6.C[i]=q(h){v h==Q?7.D?7[0][n]:M:7.1t(n,h)}});6.W(6.2e.1f,q(i,n){6.C[n]=q(h){v h==Q?(7.D?6.1f(7[0],n):M):7.1f(n,h)}})},W:q(15,C,1g){l(15.D==Q)I(u i 1r 15)C.16(15[i],1g||[i,15[i]]);G I(u i=0;i<15.D;i++)l(C.16(15[i],1g||[i,15[i]])===U)45;v 15},1j:{2g:q(o,c){l(6.1j.3t(o,c))v;o.1j+=(o.1j?" ":"")+c},25:q(o,c){l(!c){o.1j=""}G{u 2L=o.1j.3b(" ");I(u i=0;i<2L.D;i++){l(2L[i]==c){2L.67(i,1);45}}o.1j=2L.5Z(\' \')}},3t:q(e,a){l(e.1j!=Q)e=e.1j;v 1h 43("(^|\\\\s)"+a+"(\\\\s|$)").28(e)}},4A:q(e,o,f){I(u i 1r o){e.1a["1Z"+i]=e.1a[i];e.1a[i]=o[i]}f.16(e,[]);I(u i 1r o)e.1a[i]=e.1a["1Z"+i]},1f:q(e,p){l(p=="1G"||p=="2c"){u 1Z={},3K,3F,d=["68","6O","69","7c"];I(u i 1r d){1Z["6b"+d[i]]=0;1Z["6c"+d[i]+"6e"]=0}6.4A(e,1Z,q(){l(6.1f(e,"1u")!="20"){3K=e.6f;3F=e.6g}G{e=6(e.3f(T)).1V(":3W").5u("2J").4q().1f({3U:"1S",2H:"6i",1u:"2F",6j:"0",5l:"0"}).4H(e.1i)[0];u 31=6.1f(e.1i,"2H");l(31==""||31=="3R")e.1i.1a.2H="6k";3K=e.6l;3F=e.6m;l(31==""||31=="3R")e.1i.1a.2H="3R";e.1i.3s(e)}});v p=="1G"?3K:3F}v 6.26(e,p)},26:q(F,E,4I){u L;l(E==\'1m\'&&6.11.1p)v 6.1t(F.1a,\'1m\');l(E=="3p"||E=="2y")E=6.11.1p?"37":"2y";l(!4I&&F.1a[E]){L=F.1a[E]}G l(F.34){u 5S=E.1B(/\\-(\\w)/g,q(m,c){v c.2b()});L=F.34[E]||F.34[5S]}G l(17.3g&&17.3g.4u){l(E=="2y"||E=="37")E="3p";E=E.1B(/([A-Z])/g,"-$1").4d();u 1l=17.3g.4u(F,M);l(1l)L=1l.5P(E);G l(E==\'1u\')L=\'20\';G 6.4A(F,{1u:\'2F\'},q(){L=17.3g.4u(7,M).5P(E)})}v L},3D:q(a){u r=[];I(u i=0;i<a.D;i++){u 1M=a[i];l(1M.O==1C){u s=6.2K(1M),21=17.5N("21"),1Y=[0,"",""];l(!s.1b("<6v"))1Y=[1,"<3c>","</3c>"];G l(!s.1b("<6w")||!s.1b("<29"))1Y=[1,"<22>","</22>"];G l(!s.1b("<4t"))1Y=[2,"<22>","</22>"];G l(!s.1b("<6x")||!s.1b("<6z"))1Y=[3,"<22><29><4t>","</4t></29></22>"];21.2V=1Y[1]+s+1Y[2];24(1Y[0]--)21=21.2a;I(u j=0;j<21.2f.D;j++)r.1k(21.2f[j])}G l(1M.D!=Q&&!1M.1R)I(u n=0;n<1M.D;n++)r.1k(1M[n]);G r.1k(1M.1R?1M:17.6A(1M.6C()))}v r},2u:{"":"m[2]== \'*\'||a.2p.2b()==m[2].2b()","#":"a.3a(\'3H\')&&a.3a(\'3H\')==m[2]",":":{5o:"i<m[3]-0",5X:"i>m[3]-0",5L:"m[3]-0==i",5n:"m[3]-0==i",2h:"i==0",1N:"i==r.D-1",52:"i%2==0",53:"i%2","5L-3x":"6.1x(a,m[3]).1l","2h-3x":"6.1x(a,0).1l","1N-3x":"6.1x(a,0).1N","6D-3x":"6.1x(a).D==1",5s:"a.2f.D",5B:"!a.2f.D",5p:"6.C.2B.16([a]).1b(m[3])>=0",6E:"a.B!=\'1S\'&&6.1f(a,\'1u\')!=\'20\'&&6.1f(a,\'3U\')!=\'1S\'",1S:"a.B==\'1S\'||6.1f(a,\'1u\')==\'20\'||6.1f(a,\'3U\')==\'1S\'",6F:"!a.2P",2P:"a.2P",2J:"a.2J",3V:"a.3V || 6.1t(a, \'3V\')",2B:"a.B==\'2B\'",3W:"a.B==\'3W\'",5y:"a.B==\'5y\'",3Q:"a.B==\'3Q\'",5v:"a.B==\'5v\'",4x:"a.B==\'4x\'",5w:"a.B==\'5w\'",4w:"a.B==\'4w\'",4s:"a.B==\'4s\'",5z:"a.2p.4d().4T(/5z|3c|6L|4s/)"},".":"6.1j.3t(a,m[2])","@":{"=":"z==m[4]","!=":"z!=m[4]","^=":"z && !z.1b(m[4])","$=":"z && z.32(z.D - m[4].D,m[4].D)==m[4]","*=":"z && z.1b(m[4])>=0","":"z"},"[":"6.1V(m[2],a).D"},3B:["\\\\.\\\\.|/\\\\.\\\\.","a.1i",">|/","6.1x(a.2a)","\\\\+","6.1x(a).3z","~",q(a){u r=[];u s=6.1x(a);l(s.n>0)I(u i=s.n;i<s.D;i++)r.1k(s[i]);v r}],1V:q(t,1o){l(1o&&1o.1R==Q)1o=M;1o=1o||6.1o||17;l(t.O!=1C)v[t];l(!t.1b("//")){1o=1o.4Q;t=t.32(2,t.D)}G l(!t.1b("/")){1o=1o.4Q;t=t.32(1,t.D);l(t.1b("/")>=1)t=t.32(t.1b("/"),t.D)}u L=[1o];u 1K=[];u 1N=M;24(t.D>0&&1N!=t){u r=[];1N=t;t=6.2K(t).1B(/^\\/\\//i,"");u 36=U;I(u i=0;i<6.3B.D;i+=2){l(36)51;u 2v=1h 43("^("+6.3B[i]+")");u m=2v.3d(t);l(m){r=L=6.2r(L,6.3B[i+1]);t=6.2K(t.1B(2v,""));36=T}}l(!36){l(!t.1b(",")||!t.1b("|")){l(L[0]==1o)L.4h();1K=6.1X(1K,L);r=L=[1o];t=" "+t.32(1,t.D)}G{u 3Z=/^([#.]?)([a-4Y-9\\\\*44-]*)/i;u m=3Z.3d(t);l(m[1]=="#"){u 4l=17.5V(m[2]);r=L=4l?[4l]:[];t=t.1B(3Z,"")}G{l(!m[2]||m[1]==".")m[2]="*";I(u i=0;i<L.D;i++)r=6.1X(r,m[2]=="*"?6.40(L[i]):L[i].4S(m[2]))}}}l(t){u 1D=6.1c(t,r);L=r=1D.r;t=6.2K(1D.t)}}l(L&&L[0]==1o)L.4h();1K=6.1X(1K,L);v 1K},40:q(o,r){r=r||[];u s=o.2f;I(u i=0;i<s.D;i++)l(s[i].1R==1){r.1k(s[i]);6.40(s[i],r)}v r},1t:q(F,1d,Y){u 2m={"I":"7v","6P":"1j","3p":6.11.1p?"37":"2y",2y:6.11.1p?"37":"2y",2V:"2V",1j:"1j",Y:"Y",2P:"2P",2J:"2J",6R:"6S"};l(1d=="1m"&&6.11.1p&&Y!=Q){F[\'6U\']=1;l(Y==1)v F["1c"]=F["1c"].1B(/3k\\([^\\)]*\\)/5c,"");G v F["1c"]=F["1c"].1B(/3k\\([^\\)]*\\)/5c,"")+"3k(1m="+Y*4U+")"}G l(1d=="1m"&&6.11.1p){v F["1c"]?4c(F["1c"].4T(/3k\\(1m=(.*)\\)/)[1])/4U:1}l(1d=="1m"&&6.11.2I&&Y==1)Y=0.6W;l(2m[1d]){l(Y!=Q)F[2m[1d]]=Y;v F[2m[1d]]}G l(Y==Q&&6.11.1p&&F.2p&&F.2p.2b()==\'6X\'&&(1d==\'7f\'||1d==\'7e\')){v F.70(1d).4Z}G l(F.3a!=Q&&F.7b){l(Y!=Q)F.72(1d,Y);v F.3a(1d)}G{1d=1d.1B(/-([a-z])/73,q(z,b){v b.2b()});l(Y!=Q)F[1d]=Y;v F[1d]}},4X:["\\\\[ *(@)S *([!*$^=]*) *(\'?\\"?)(.*?)\\\\4 *\\\\]","(\\\\[)\\s*(.*?)\\s*\\\\]","(:)S\\\\(\\"?\'?([^\\\\)]*?)\\"?\'?\\\\)","([:.#]*)S"],1c:q(t,r,2t){u g=2t!==U?6.2O:q(a,f){v 6.2O(a,f,T)};24(t&&/^[a-z[({<*:.#]/i.28(t)){u p=6.4X;I(u i=0;i<p.D;i++){u 2v=1h 43("^"+p[i].1B("S","([a-z*44-][a-4Y-76-]*)"),"i");u m=2v.3d(t);l(m){l(!i)m=["",m[1],m[3],m[2],m[5]];t=t.1B(2v,"");45}}l(m[1]==":"&&m[2]=="2t")r=6.1c(m[3],r,U).r;G{u f=6.2u[m[1]];l(f.O!=1C)f=6.2u[m[1]][m[2]];3A("f = q(a,i){"+(m[1]=="@"?"z=6.1t(a,m[3]);":"")+"v "+f+"}");r=g(r,f)}}v{r:r,t:t}},2K:q(t){v t.1B(/^\\s+|\\s+$/g,"")},3L:q(F){u 47=[];u 1l=F.1i;24(1l&&1l!=17){47.1k(1l);1l=1l.1i}v 47},1x:q(F,2j,2t){u 14=[];l(F){u 2k=F.1i.2f;I(u i=0;i<2k.D;i++){l(2t===T&&2k[i]==F)51;l(2k[i].1R==1)14.1k(2k[i]);l(2k[i]==F)14.n=14.D-1}}v 6.1y(14,{1N:14.n==14.D-1,1l:2j=="52"&&14.n%2==0||2j=="53"&&14.n%2||14[2j]==F,4j:14[14.n-1],3z:14[14.n+1]})},1X:q(2h,35){u 1J=[];I(u k=0;k<2h.D;k++)1J[k]=2h[k];I(u i=0;i<35.D;i++){u 48=T;I(u j=0;j<2h.D;j++)l(35[i]==2h[j])48=U;l(48)1J.1k(35[i])}v 1J},2O:q(14,C,4a){l(C.O==1C)C=1h 1v("a","i","v "+C);u 1J=[];I(u i=0;i<14.D;i++)l(!4a&&C(14[i],i)||4a&&!C(14[i],i))1J.1k(14[i]);v 1J},2r:q(14,C){l(C.O==1C)C=1h 1v("a","v "+C);u 1J=[];I(u i=0;i<14.D;i++){u 1D=C(14[i],i);l(1D!==M&&1D!=Q){l(1D.O!=2z)1D=[1D];1J=6.1X(1J,1D)}}v 1J},J:{2g:q(P,B,1H){l(6.11.1p&&P.42!=Q)P=1z;l(!1H.2q)1H.2q=7.2q++;l(!P.1E)P.1E={};u 2W=P.1E[B];l(!2W){2W=P.1E[B]={};l(P["2N"+B])2W[0]=P["2N"+B]}2W[1H.2q]=1H;P["2N"+B]=7.58;l(!7.1e[B])7.1e[B]=[];7.1e[B].1k(P)},2q:1,1e:{},25:q(P,B,1H){l(P.1E)l(B&&P.1E[B])l(1H)57 P.1E[B][1H.2q];G I(u i 1r P.1E[B])57 P.1E[B][i];G I(u j 1r P.1E)7.25(P,j)},1P:q(B,K,P){K=K||[];l(!P){u g=7.1e[B];l(g)I(u i=0;i<g.D;i++)7.1P(B,K,g[i])}G l(P["2N"+B]){K.59(7.2m({B:B,2G:P}));P["2N"+B].16(P,K)}},58:q(J){l(1T 6=="Q")v U;J=J||6.J.2m(1z.J);l(!J)v U;u 3m=T;u c=7.1E[J.B];u 1g=[].7h.3O(19,1);1g.59(J);I(u j 1r c){l(c[j].16(7,1g)===U){J.4p();J.5a();3m=U}}v 3m},2m:q(J){l(6.11.1p){J=1z.J;J.2G=J.7i}G l(6.11.2M&&J.2G.1R==3){J=6.1y({},J);J.2G=J.2G.1i}J.4p=q(){7.3m=U};J.5a=q(){7.7l=T};v J}}});1h q(){u b=5I.5K.4d();6.11={2M:/5e/.28(b),30:/30/.28(b),1p:/1p/.28(b)&&!/30/.28(b),2I:/2I/.28(b)&&!/(7m|5e)/.28(b)};6.7n=!6.11.1p||17.7o=="7p"};6.2e={2o:{4H:"5g",7q:"5h",2Y:"5i",7r:"5j"},1f:"2c,1G,7s,5l,2H,3p,3h,7t,7u".3b(","),1c:["5n","5o","5X","5p"],1t:{1D:"Y",38:"2V",3H:M,7x:M,1d:M,7z:M,3w:M,7A:M},5r:{5s:"a.1i",7B:6.3L,3L:6.3L,3z:"6.1x(a).3z",4j:"6.1x(a).4j",2k:"6.1x(a, M, T)",7C:"6.1x(a.2a)"},W:{5u:q(1L){7.7E(1L)},1A:q(){7.1a.1u=7.2A?7.2A:"";l(6.1f(7,"1u")=="20")7.1a.1u="2F"},1s:q(){7.2A=7.2A||6.1f(7,"1u");l(7.2A=="20")7.2A="2F";7.1a.1u="20"},4o:q(){6(7)[6(7).4E(":1S")?"1A":"1s"].16(6(7),19)},7F:q(c){6.1j.2g(7,c)},7G:q(c){6.1j.25(7,c)},7H:q(c){6.1j[6.1j.3t(7,c)?"25":"2g"](7,c)},25:q(a){l(!a||6.1c(a,[7]).r)7.1i.3s(7)},5B:q(){24(7.2a)7.3s(7.2a)},2Z:q(B,C){l(C.O==1C)C=1h 1v("e",(!C.1b(".")?"6(7)":"v ")+C);6.J.2g(7,B,C)},4C:q(B,C){6.J.25(7,B,C)},1P:q(B,K){6.J.1P(B,K,7)}}};6.5C();6.C.1y({5E:6.C.4o,4o:q(a,b){v a&&b&&a.O==1v&&b.O==1v?7.5M(q(e){7.1N=7.1N==a?b:a;e.4p();v 7.1N.16(7,[e])||U}):7.5E.16(7,19)},7K:q(f,g){q 4r(e){u p=(e.B=="3C"?e.7M:e.7N)||e.7O;24(p&&p!=7)3u{p=p.1i}3o(e){p=7};l(p==7)v U;v(e.B=="3C"?f:g).16(7,[e])}v 7.3C(4r).5Q(4r)},1W:q(f){l(6.3y)f.16(17);G{6.2C.1k(f)}v 7}});6.1y({3y:U,2C:[],1W:q(){l(!6.3y){6.3y=T;l(6.2C){I(u i=0;i<6.2C.D;i++)6.2C[i].16(17);6.2C=M}l(6.11.2I||6.11.30)17.7P("5T",6.1W,U)}}});1h q(){u e=("7R,7S,2S,7T,7U,4z,5M,7V,"+"7X,7Y,81,3C,5Q,83,4w,3c,"+"4x,86,87,88,2l").3b(",");I(u i=0;i<e.D;i++)1h q(){u o=e[i];6.C[o]=q(f){v f?7.2Z(o,f):7.1P(o)};6.C["89"+o]=q(f){v 7.4C(o,f)};6.C["8a"+o]=q(f){u P=6(7);u 1H=q(){P.4C(o,1H);P=M;f.16(7,19)};v 7.2Z(o,1H)}};l(6.11.2I||6.11.30){17.8c("5T",6.1W,U)}G l(6.11.1p){17.8d("<8e"+"8f 3H=5W 8n=T "+"3w=//:><\\/27>");u 27=17.5V("5W");27.2w=q(){l(7.3n!="1I")v;7.1i.3s(7);6.1W()};27=M}G l(6.11.2M){6.3N=42(q(){l(17.3n=="63"||17.3n=="1I"){56(6.3N);6.3N=M;6.1W()}},10)}6.J.2g(1z,"2S",6.1W)};l(6.11.1p)6(1z).4z(q(){u J=6.J,1e=J.1e;I(u B 1r 1e){u 3P=1e[B],i=3P.D;l(i>0)6a l(B!=\'4z\')J.25(3P[i-1],B);24(--i)}});6.C.1y({60:6.C.1A,1A:q(12,H){v 12?7.1U({1G:"1A",2c:"1A",1m:"1A"},12,H):7.60()},5U:6.C.1s,1s:q(12,H){v 12?7.1U({1G:"1s",2c:"1s",1m:"1s"},12,H):7.5U()},6n:q(12,H){v 7.1U({1G:"1A"},12,H)},6o:q(12,H){v 7.1U({1G:"1s"},12,H)},6p:q(12,H){v 7.W(q(){u 4J=6(7).4E(":1S")?"1A":"1s";6(7).1U({1G:4J},12,H)})},6r:q(12,H){v 7.1U({1m:"1A"},12,H)},6s:q(12,H){v 7.1U({1m:"1s"},12,H)},6t:q(12,2o,H){v 7.1U({1m:2o},12,H)},1U:q(E,12,H){v 7.1w(q(){7.2U=6.1y({},E);I(u p 1r E){u e=1h 6.2R(7,6.12(12,H),p);l(E[p].O==4O)e.3e(e.1l(),E[p]);G e[E[p]](E)}})},1w:q(B,C){l(!C){C=B;B="2R"}v 7.W(q(){l(!7.1w)7.1w={};l(!7.1w[B])7.1w[B]=[];7.1w[B].1k(C);l(7.1w[B].D==1)C.16(7)})}});6.1y({5f:q(e,p){l(e.5F)v;l(p=="1G"&&e.4L!=3l(6.26(e,p)))v;l(p=="2c"&&e.4M!=3l(6.26(e,p)))v;u a=e.1a[p];u o=6.26(e,p,1);l(p=="1G"&&e.4L!=o||p=="2c"&&e.4M!=o)v;e.1a[p]=e.34?"":"5H";u n=6.26(e,p,1);l(o!=n&&n!="5H"){e.1a[p]=a;e.5F=T}},12:q(s,o){o=o||{};l(o.O==1v)o={1I:o};u 5D={6G:6H,6J:4K};o.2E=(s&&s.O==4O?s:5D[s])||5k;o.3J=o.1I;o.1I=q(){6.4R(7,"2R");l(o.3J&&o.3J.O==1v)o.3J.16(7)};v o},1w:{},4R:q(F,B){B=B||"2R";l(F.1w&&F.1w[B]){F.1w[B].4h();u f=F.1w[B][0];l(f)f.16(F)}},2R:q(F,2x,E){u z=7;z.o={2E:2x.2E||5k,1I:2x.1I,2s:2x.2s};z.V=F;u y=z.V.1a;z.a=q(){l(2x.2s)2x.2s.16(F,[z.2i]);l(E=="1m")6.1t(y,"1m",z.2i);G l(3l(z.2i))y[E]=3l(z.2i)+"5d";y.1u="2F"};z.61=q(){v 4c(6.1f(z.V,E))};z.1l=q(){u r=4c(6.26(z.V,E));v r&&r>-6Z?r:z.61()};z.3e=q(41,2o){z.3M=(1h 54()).55();z.2i=41;z.a();z.49=42(q(){z.2s(41,2o)},13)};z.1A=q(){l(!z.V.1Q)z.V.1Q={};z.V.1Q[E]=7.1l();z.3e(0,z.V.1Q[E]);l(E!="1m")y[E]="77"};z.1s=q(){l(!z.V.1Q)z.V.1Q={};z.V.1Q[E]=7.1l();z.o.1s=T;z.3e(z.V.1Q[E],0)};l(!z.V.4b)z.V.4b=6.1f(z.V,"3h");y.3h="1S";z.2s=q(4B,4g){u t=(1h 54()).55();l(t>z.o.2E+z.3M){56(z.49);z.49=M;z.2i=4g;z.a();z.V.2U[E]=T;u 1K=T;I(u i 1r z.V.2U)l(z.V.2U[i]!==T)1K=U;l(1K){y.3h=z.V.4b;l(z.o.1s)y.1u=\'20\';l(z.o.1s){I(u p 1r z.V.2U){l(p=="1m")6.1t(y,p,z.V.1Q[p]);G y[p]=z.V.1Q[p]+"5d";l(p==\'1G\'||p==\'2c\')6.5f(z.V,p)}}}l(1K&&z.o.1I&&z.o.1I.O==1v)z.o.1I.16(z.V)}G{u p=(t-7.3M)/z.o.2E;z.2i=((-5q.7w(p*5q.7y)/2)+0.5)*(4g-4B)+4B;z.a()}}}});6.C.1y({7D:q(N,1O,H){7.2S(N,1O,H,1)},2S:q(N,1O,H,1F){l(N.O==1v)v 7.2Z("2S",N);H=H||q(){};u B="3T";l(1O){l(1O.O==1v){H=1O;1O=M}G{1O=6.2Q(1O);B="4W"}}u 4m=7;6.3I(B,N,1O,q(3v,18){l(18=="2D"||!1F&&18=="5m"){4m.38(3v.3G).3X().W(H,[3v.3G,18])}G H.16(4m,[3v.3G,18])},1F);v 7},7J:q(){v 6.2Q(7)},3X:q(){v 7.1V(\'27\').W(q(){l(7.3w)6.5Y(7.3w,q(){});G 3A.3O(1z,7.2B||7.7L||7.2V||"")}).4q()}});l(6.11.1p&&1T 3i=="Q")3i=q(){v 1h 7Q(5I.5K.1b("7W 5")>=0?"82.5R":"84.5R")};1h q(){u e="5O,5G,5A,5x,5t".3b(",");I(u i=0;i<e.D;i++)1h q(){u o=e[i];6.C[o]=q(f){v 7.2Z(o,f)}}};6.1y({1n:q(N,K,H,B,1F){l(K&&K.O==1v){B=H;H=K;K=M}l(K)N+=((N.1b("?")>-1)?"&":"?")+6.2Q(K);6.3I("3T",N,M,q(r,18){l(H)H(6.3r(r,B),18)},1F)},8h:q(N,K,H,B){6.1n(N,K,H,B,1)},5Y:q(N,H){l(H)6.1n(N,M,H,"27");G{6.1n(N,M,M,"27")}},64:q(N,K,H){l(H)6.1n(N,K,H,"3S");G{6.1n(N,K,"3S")}},8o:q(N,K,H,B){6.3I("4W",N,6.2Q(K),q(r,18){l(H)H(6.3r(r,B),18)})},1q:0,6h:q(1q){6.1q=1q},39:{},3I:q(B,N,K,L,1F){u 1e=T;u 1q=6.1q;l(!N){L=B.1I;u 2D=B.2D;u 2l=B.2l;u 4k=B.4k;u 1e=1T B.1e=="6q"?B.1e:T;u 1q=1T B.1q=="6u"?B.1q:6.1q;1F=B.1F||U;K=B.K;N=B.N;B=B.B}l(1e&&!6.4v++)6.J.1P("5O");u 4y=U;u R=1h 3i();R.6B(B||"3T",N,T);l(K)R.3j("6I-6K","6M/x-6N-6Q-6T");l(1F)R.3j("6V-3Y-6Y",6.39[N]||"71, 74 75 78 46:46:46 79");R.3j("X-7a-7d","3i");l(R.7g)R.3j("7j","7k");u 2w=q(4F){l(R&&(R.3n==4||4F=="1q")){4y=T;u 18=6.4G(R)&&4F!="1q"?1F&&6.4N(R,N)?"5m":"2D":"2l";l(18!="2l"){u 3q;3u{3q=R.4i("4P-3Y")}3o(e){}l(1F&&3q)6.39[N]=3q;l(2D)2D(6.3r(R,4k),18);l(1e)6.J.1P("5t")}G{l(2l)2l(R,18);l(1e)6.J.1P("5x")}l(1e)6.J.1P("5A");l(1e&&!--6.4v)6.J.1P("5G");l(L)L(R,18);R.2w=q(){};R=M}};R.2w=2w;l(1q>0)7Z(q(){l(R){R.85();l(!4y)2w("1q");R=M}},1q);R.8i(K)},4v:0,4G:q(r){3u{v!r.18&&66.6d=="3Q:"||(r.18>=4K&&r.18<6y)||r.18==5b||6.11.2M&&r.18==Q}3o(e){}v U},4N:q(R,N){3u{u 4V=R.4i("4P-3Y");v R.18==5b||4V==6.39[N]||6.11.2M&&R.18==Q}3o(e){}v U},3r:q(r,B){u 4n=r.4i("7I-B");u K=!B&&4n&&4n.1b("R")>=0;K=B=="R"||K?r.80:r.3G;l(B=="27")3A.3O(1z,K);l(B=="3S")3A("K = "+K);l(B=="38")$("<21>").38(K).3X();v K},2Q:q(a){u s=[];l(a.O==2z||a.3E){I(u i=0;i<a.D;i++)s.1k(a[i].1d+"="+50(a[i].Y))}G{I(u j 1r a)s.1k(j+"="+50(a[j]))}v s.5Z("&")}})}',62,521,'||||||jQuery|this||||||||||||||if|||||function||||var|return||||||type|fn|length|prop|elem|else|callback|for|event|data|ret|null|url|constructor|element|undefined|xml||true|false|el|each||value|||browser|speed||elems|obj|apply|document|status|arguments|style|indexOf|filter|name|global|css|args|new|parentNode|className|push|cur|opacity|get|context|msie|timeout|in|hide|attr|display|Function|queue|sibling|extend|window|show|replace|String|val|events|ifModified|height|handler|complete|result|done|key|arg|last|params|trigger|orig|nodeType|hidden|typeof|animate|find|ready|merge|wrap|old|none|div|table|num|while|remove|curCSS|script|test|tbody|firstChild|toUpperCase|width|fn2|macros|childNodes|add|first|now|pos|siblings|error|fix|pushStack|to|nodeName|guid|map|step|not|expr|re|onreadystatechange|options|cssFloat|Array|oldblock|text|readyList|success|duration|block|target|position|mozilla|checked|trim|classes|safari|on|grep|disabled|param|fx|load|domManip|curAnim|innerHTML|handlers|dir|insertBefore|bind|opera|parPos|substr|stack|currentStyle|second|foundToken|styleFloat|html|lastModified|getAttribute|split|select|exec|custom|cloneNode|defaultView|overflow|XMLHttpRequest|setRequestHeader|alpha|parseInt|returnValue|readyState|catch|float|modRes|httpData|removeChild|has|try|res|src|child|isReady|next|eval|token|mouseover|clean|jquery|oWidth|responseText|id|ajax|oldComplete|oHeight|parents|startTime|safariTimer|call|els|file|static|json|GET|visibility|selected|radio|evalScripts|Modified|re2|getAll|from|setInterval|RegExp|_|break|00|matched|noCollision|timer|inv|oldOverflow|parseFloat|toLowerCase|appendChild|clone|lastNum|shift|getResponseHeader|prev|dataType|oid|self|ct|toggle|preventDefault|end|handleHover|button|tr|getComputedStyle|active|reset|submit|requestDone|unload|swap|firstNum|unbind|deep|is|istimeout|httpSuccess|appendTo|force|state|200|scrollHeight|scrollWidth|httpNotModified|Number|Last|documentElement|dequeue|getElementsByTagName|match|100|xmlRes|POST|parse|z0|nodeValue|encodeURIComponent|continue|even|odd|Date|getTime|clearInterval|delete|handle|unshift|stopPropagation|304|gi|px|webkit|setAuto|append|prepend|before|after|400|left|notmodified|eq|lt|contains|Math|axis|parent|ajaxSuccess|removeAttr|password|image|ajaxError|checkbox|input|ajaxComplete|empty|init|ss|_toggle|notAuto|ajaxStop|auto|navigator|size|userAgent|nth|click|createElement|ajaxStart|getPropertyValue|mouseout|XMLHTTP|newProp|DOMContentLoaded|_hide|getElementById|__ie_init|gt|getScript|join|_show|max|THEAD|loaded|getJSON|initDone|location|splice|Top|Right|do|padding|border|protocol|Width|offsetHeight|offsetWidth|ajaxTimeout|absolute|right|relative|clientHeight|clientWidth|slideDown|slideUp|slideToggle|boolean|fadeIn|fadeOut|fadeTo|number|opt|thead|td|300|th|createTextNode|open|toString|only|visible|enabled|slow|600|Content|fast|Type|textarea|application|www|Bottom|class|form|readonly|readOnly|urlencoded|zoom|If|9999|FORM|Since|10000|getAttributeNode|Thu|setAttribute|ig|01|Jan|9_|1px|1970|GMT|Requested|tagName|Left|With|method|action|overrideMimeType|slice|srcElement|Connection|close|cancelBubble|compatible|boxModel|compatMode|CSS1Compat|prependTo|insertAfter|top|color|background|htmlFor|cos|title|PI|href|rel|ancestors|children|loadIfModified|removeAttribute|addClass|removeClass|toggleClass|content|serialize|hover|textContent|fromElement|toElement|relatedTarget|removeEventListener|ActiveXObject|blur|focus|resize|scroll|dblclick|MSIE|mousedown|mouseup|setTimeout|responseXML|mousemove|Microsoft|change|Msxml2|abort|keydown|keypress|keyup|un|one|prototype|addEventListener|write|scr|ipt|index|getIfModified|send|nextSibling|pop|Boolean|TABLE|defer|post'.split('|'),0,{}))


/*
 * jQuery form plugin
 * @requires jQuery v1.0.2
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id$
 */

/**
 * ajaxSubmit() provides a mechanism for submitting an HTML form using AJAX.
 *
 * ajaxSubmit accepts a single argument which can be either a success callback function
 * or an options Object.  If a function is provided it will be invoked upon successful
 * completion of the submit and will be passed the response from the server.
 * If an options Object is provided, the following attributes are supported:
 *
 *  target:   Identifies the element(s) in the page to be updated with the server response.
 *            This value may be specified as a jQuery selection string, a jQuery object,
 *            or a DOM element.
 *            default value: null
 *
 *  url:      URL to which the form data will be submitted.
 *            default value: value of form's 'action' attribute
 *
 *  method:   The method in which the form data should be submitted, 'GET' or 'POST'.
 *            default value: value of form's 'method' attribute (or 'GET' if none found)
 *
 *  before:   @deprecated use 'beforeSubmit'
 *  beforeSubmit:  Callback method to be invoked before the form is submitted.
 *            default value: null
 *
 *  after:    @deprecated use 'success'
 *  success:  Callback method to be invoked after the form has been successfully submitted
 *            and the response has been returned from the server
 *            default value: null
 *
 *  dataType: Expected dataType of the response.  One of: null, 'xml', 'script', or 'json'
 *            default value: null
 *
 *  semantic: Boolean flag indicating whether data must be submitted in semantic order (slower).
 *            default value: false
 *
 *
 * The 'beforeSubmit' callback can be provided as a hook for running pre-submit logic or for
 * validating the form data.  If the 'beforeSubmit' callback returns false then the form will
 * not be submitted. The 'beforeSubmit' callback is invoked with three arguments: the form data
 * in array format, the jQuery object, and the options object passed into ajaxSubmit.  
 * The form data array takes the following form:
 *
 *     [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * If a 'success' callback method is provided it is invoked after the response has been returned
 * from the server.  It is passed the responseText or responseXML value (depending on dataType).
 * See jQuery.ajax for further details.
 *
 *
 * The dataType option provides a means for specifying how the server response should be handled.
 * This maps directly to the jQuery.httpData method.  The following values are supported:
 * 
 *      'xml':    if dataType == 'xml' the server response is treated as XML and the 'after'
 *                   callback method, if specified, will be passed the responseXML value
 *      'json':   if dataType == 'json' the server response will be evaluted and passed to
 *                   the 'after' callback, if specified
 *      'script': if dataType == 'script' the server response is evaluated in the global context
 *
 *
 * Note that it does not make sense to use both the 'target' and 'dataType' options.  If both
 * are provided the target will be ignored.
 *
 * The semantic argument can be used to force form serialization in semantic order.  If your
 * form must be submitted with name/value pairs in semantic order then pass true for this arg,
 * otherwise pass false (or nothing) to avoid the overhead for this logic (which can be
 * significant for very large forms).
 *
 * When used on its own, ajaxSubmit() is typically bound to a form's submit event like this:
 *
 * $("#form-id").submit(function() {
 *     $(this).ajaxSubmit(options);
 *     return false; // cancel conventional submit
 * });
 *
 * When using ajaxForm(), however, this is done for you.
 *
 * @example
 * $('#myForm').ajaxSubmit(function(data) {
 *     alert('Form submit succeeded! Server returned: ' + data);
 * });
 * @desc Submit form and alert server response
 *
 *
 * @example
 * var options = {
 *     target: '#myTargetDiv'
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc Submit form and update page element with server response
 *
 *
 * @example
 * var options = {
 *     success: function(responseText) {
 *         alert(responseText);
 *     }
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc Submit form and alert the server response
 *
 *
 * @example
 * var options = {
 *     beforeSubmit: function(formArray, jqForm) {
 *         if (formArray.length == 0) {
 *             alert('Please enter data.');
 *             return false;
 *         }
 *     }
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc Pre-submit validation which aborts the submit operation if form data is empty
 *
 *
 * @example
 * var options = {
 *     url: myJsonUrl.php,
 *     dataType: 'json',
 *     success: function(data) {
 *        // 'data' is an object representing the the evaluated json data
 *     }
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc json data returned and evaluated
 *
 *
 * @example
 * var options = {
 *     url: myXmlUrl.php,
 *     dataType: 'xml',
 *     success: function(responseXML) {
 *        // responseXML is XML document object
 *        var data = $('myElement', responseXML).text();
 *     }
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc XML data returned from server
 *
 *
 * @example
 * $('#myForm).submit(function() {
 *    $(this).ajaxSubmit();
 *    return false;
 * });
 * @desc Bind form's submit event to use ajaxSubmit
 *
 *
 * @name ajaxSubmit
 * @type jQuery
 * @param options  object literal containing options which control the form submission process
 * @cat Plugins/Form
 * @return jQuery
 * @see formToArray
 * @see ajaxForm
 * @see $.ajax
 * @author jQuery Community
 */
jQuery.fn.ajaxSubmit = function(options) {
    if (typeof options == 'function')
        options = { success: options };

    options = jQuery.extend({
        url:    this.attr('action') || '',
        method: this.attr('method') || 'GET'
    }, options || {});

    // 'before' and 'after' are deprecated (temporarily remap them)
    options.success = options.success || options.after;
    options.beforeSubmit = options.beforeSubmit || options.before;

    var a = this.formToArray(options.semantic);

    // give pre-submit callback an opportunity to abort the submit
    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) return;

    var q = jQuery.param(a);
    var get = (options.method && options.method.toUpperCase() == 'GET');

    if (get)
        // if url already has a '?' then append args after '&'
        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;

    // remap 'method' to 'type' for the ajax method
    options.type = options.method;
    options.data = get ? null : q;  // data is null for 'get' or the query string for 'post'

    // perform a load on the target only if dataType is not provided
    if (!options.dataType && options.target) {
        var oldSuccess = options.success || function(){};
        options.success = function(data, status) {
            jQuery(options.target).html(data).evalScripts().each(oldSuccess, [data, status]);
        }
    }
        
    jQuery.ajax(options);
    return this;
};


/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *    is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *    used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * Note that for accurate x/y coordinates of image submit elements in all browsers
 * you need to also use the "dimensions" plugin (this method will auto-detect its presence).
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.  See ajaxSubmit for a full description of the options argument.
 *
 *
 * @example
 * var options = {
 *     target: '#myTargetDiv'
 * };
 * $('#myForm').ajaxSForm(options);
 * @desc Bind form's submit event so that 'myTargetDiv' is updated with the server response
 *       when the form is submitted.
 *
 *
 * @example
 * var options = {
 *     success: function(responseText) {
 *         alert(responseText);
 *     }
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc Bind form's submit event so that server response is alerted after the form is submitted.
 *
 *
 * @example
 * var options = {
 *     beforeSubmit: function(formArray, jqForm) {
 *         if (formArray.length == 0) {
 *             alert('Please enter data.');
 *             return false;
 *         }
 *     }
 * };
 * $('#myForm').ajaxSubmit(options);
 * @desc Bind form's submit event so that pre-submit callback is invoked before the form
 *       is submitted.
 *
 *
 * @name   ajaxForm
 * @param  options  object literal containing options which control the form submission process
 * @return jQuery
 * @cat    Plugins/Form
 * @type   jQuery
 * @see    ajaxSubmit
 * @author jQuery Community
 */
jQuery.fn.ajaxForm = function(options) {
    return this.each(function() {
        jQuery("input:submit,input:image", this).click(function(ev) {
            this.form.clk = this;

            if (ev.offsetX != undefined) {
                this.form.clk_x = ev.offsetX;
                this.form.clk_y = ev.offsetY;
            } else if (typeof jQuery.fn.offset == 'function') { // try to use dimensions plugin
                var offset = $(this).offset();
                this.form.clk_x = ev.pageX - offset.left;
                this.form.clk_y = ev.pageY - offset.top;
            } else {
                this.form.clk_x = ev.pageX - this.offsetLeft;
                this.form.clk_y = ev.pageY - this.offsetTop;
            }
        })
    }).submit(function(e) {
        jQuery(this).ajaxSubmit(options);
        return false;
    });
};


/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 *
 * The semantic argument can be used to force form serialization in semantic order.
 * If your form must be submitted with name/value pairs in semantic order then pass
 * true for this arg, otherwise pass false (or nothing) to avoid the overhead for
 * this logic (which can be significant for very large forms).
 *
 * @example var data = $("#myForm").formToArray();
 * $.post( "myscript.cgi", data );
 * @desc Collect all the data from a form and submit it to the server.
 *
 * @name formToArray
 * @param semantic true if serialization must maintain strict semantic ordering of elements (slower)
 * @type Array<Object>
 * @cat Plugins/Form
 * @see ajaxForm
 * @see ajaxSubmit
 * @author jQuery Community
 */
jQuery.fn.formToArray = function(semantic) {
    var a = [];
    var q = semantic ? ':input' : 'input,textarea,select,button';

    jQuery(q, this).each(function() {
        var n = this.name;
        var t = this.type;
        var tag = this.tagName.toLowerCase();

        if ( !n || this.disabled || t == 'reset' ||
            (t == 'checkbox' || t == 'radio') && !this.checked ||
            (t == 'submit' || t == 'image' || t == 'button') && this.form && this.form.clk != this ||
            tag == 'select' && this.selectedIndex == -1)
            return;

        if (t == 'image' && this.form.clk_x != undefined)
            return a.push(
                {name: n+'_x', value: this.form.clk_x},
                {name: n+'_y', value: this.form.clk_y}
            );

        if (tag == 'select') {
            // pass select element off to fieldValue to reuse the IE logic
            var val = jQuery.fieldValue(this, false); // pass false to optimize fieldValue
            if (t == 'select-multiple') {
                for (var i=0; i < val.length; i++)
                    a.push({name: n, value: val[i]});
            }
            else
                a.push({name: n, value: val});
        }
        else
            a.push({name: n, value: this.value});
    });
    return a;
};


/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 *
 * The semantic argument can be used to force form serialization in semantic order.
 * If your form must be submitted with name/value pairs in semantic order then pass
 * true for this arg, otherwise pass false (or nothing) to avoid the overhead for
 * this logic (which can be significant for very large forms).
 *
 * @example var data = $("#myForm").formSerialize();
 * $.ajax('POST', "myscript.cgi", data);
 * @desc Collect all the data from a form into a single string
 *
 * @name formSerialize
 * @param semantic true if serialization must maintain strict semantic ordering of elements (slower)
 * @type String
 * @cat Plugins/Form
 * @see formToArray
 * @author jQuery Community
 */
jQuery.fn.formSerialize = function(semantic) {
    //hand off to jQuery.param for proper encoding
    return jQuery.param(this.formToArray(semantic));
};


/**
 * Serializes all field elements in the jQuery object into a query string. 
 * This method will return a string in the format: name1=value1&amp;name2=value2
 *
 * The successful argument controls whether or not serialization is limited to
 * 'successful' controls (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.
 *
 * @example var data = $("input").formSerialize();
 * @desc Collect the data from all successful input elements into a query string
 *
 * @example var data = $(":radio").formSerialize();
 * @desc Collect the data from all successful radio input elements into a query string
 *
 * @example var data = $("#myForm :checkbox").formSerialize();
 * @desc Collect the data from all successful checkbox input elements in myForm into a query string
 *
 * @example var data = $("#myForm :checkbox").formSerialize(false);
 * @desc Collect the data from all checkbox elements in myForm (even the unchecked ones) into a query string
 *
 * @example var data = $(":input").formSerialize();
 * @desc Collect the data from all successful input, select, textarea and button elements into a query string
 *
 * @name fieldSerialize
 * @param successful true if only successful controls should be serialized (default is true)
 * @type String
 * @cat Plugins/Form
 */
jQuery.fn.fieldSerialize = function(successful) {
    var a = [];
    this.each(function() {
        if (!this.name) return;
        var val = jQuery.fieldValue(this, successful);
        if (val && val.constructor == Array) {
            for (var i=0; i < val.length; i++)
                a.push({name: this.name, value: val[i]});
        }
        else if (val !== null && typeof val != 'undefined')
            a.push({name: this.name, value: val});
    });
    //hand off to jQuery.param for proper encoding
    return jQuery.param(a);
};


/**
 * Returns the value of the field element in the jQuery object.  If there is more than one field element
 * in the jQuery object the value of the first successful one is returned.
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false then
 * the value of the first field element in the jQuery object is returned.
 *
 * Note: The fieldValue returned for a select-multiple element or for a checkbox input will
 *       always be an array.
 *
 * @example var data = $("#myPasswordElement").formValue();
 * @desc Gets the current value of the myPasswordElement element
 *
 * @example var data = $("#myForm :input").formValue();
 * @desc Get the value of the first successful control in the jQuery object.
 *
 * @example var data = $("#myForm :checkbox").formValue();
 * @desc Get the array of values for the first set of successful checkbox controls in the jQuery object.
 *
 * @example var data = $("#mySingleSelect").formValue();
 * @desc Get the value of the select control
 *
 * @example var data = $("#myMultiSelect").formValue();
 * @desc Get the array of selected values for the select-multiple control
 *
 * @name fieldValue
 * @param Boolean successful true if value returned must be for a successful controls (default is true)
 * @type String or Array<String>
 * @cat Plugins/Form
 */
jQuery.fn.fieldValue = function(successful) {
    var cbVal = [], cbName = null;

    // loop until we find a value
    for (var i = 0; i < this.length; i++) {
        var el = this[i];
        if (el.type == 'checkbox') {
            if (!cbName) cbName = el.name || 'unnamed';
            if (cbName != el.name) // return if we hit a checkbox with a different name
                return cbVal;
            var val = jQuery.fieldValue(el, successful);
            if (val !== null && typeof val != 'undefined') 
                cbVal.push(val);
        }
        else {
            var val = jQuery.fieldValue(el, successful);
            if (val !== null && typeof val != 'undefined') 
                return val;
        }
    }
    return cbVal;
};

/**
 * Returns the value of the field element.
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If the given element is not
 * successful and the successful arg is not false then the returned value will be null.
 *
 * Note: The fieldValue returned for a select-multiple element will always be an array.
 *
 * @example var data = jQuery.fieldValue($("#myPasswordElement")[0]);
 * @desc Gets the current value of the myPasswordElement element
 *
 * @name fieldValue
 * @param Element el The DOM element for which the value will be returned
 * @param Boolean successful true if value returned must be for a successful controls (default is true)
 * @type String or Array<String>
 * @cat Plugins/Form
 */
jQuery.fieldValue = function(el, successful) {
    var n = el.name;
    var t = el.type;
    var tag = el.tagName.toLowerCase();
    if (typeof successful == 'undefined') successful = true;

    if (successful && ( !n || el.disabled || t == 'reset' ||
        (t == 'checkbox' || t == 'radio') && !el.checked ||
        (t == 'submit' || t == 'image' || t == 'button') && el.form && el.form.clk != el ||
        tag == 'select' && el.selectedIndex == -1))
            return null;
    
    if (tag == 'select') {
        var a = [];
        for(var i=0; i < el.options.length; i++) {
            var op = el.options[i];
            if (op.selected) {
                // extra pain for IE...
                var v = jQuery.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
                if (t == 'select-one')
                    return v;
                a.push(v);
            }
        }
        return a;
    }
    return el.value;
};


$(document).ready(function() {
	if ($("#left_rating ul.rating ul").size() == 0) {
        $("#left_button").hide();    
    }
	if ($("#right_rating ul.rating ul").size() == 0) {
        $("#right_button").hide();    
    }
});

function toggleExpand() {
   $(" ul.rating ul").toggle();
   var eb = $(".toggleExpandButton");
   if (eb.length > 0) {
   	var text = eb.html().indexOf ('expand') == -1 ? 'expand ratings' : 'collapse ratings';
   	eb.html (text);
   }
}
function toggleComment() {
    $(" strong.button ul").toggle();
}


function showPostForm(){    
    if (userid == 0 && anoncancomment == 0) {
        login('loginformplaceholder', 'showPostForm();', JS_LOGIN_WITH_UN_PASS);
        return false;
    }
    
    hideLoginForm();
    hideLoginForm2();
    document.getElementById('loginformplaceholder').style.display = 'none';
    
    var spc = document.getElementById('spellCheckDiv0');
    if (spc) {
        spc.style.width = '99.5%';
    }
    document.getElementById('name_labels').style.display = 'none';
    document.getElementById('name_inputs').style.display = 'none';
    document.getElementById('postcommentformplaceholder').style.display = 'block';
}

function submitLoginForm(form, callback){    
    form.elements['login'].disabled=true; 
        
    var om = new OverlayMessage( );
    om.Set(JS_LOGGING);
    
    (new Articles_ActionHandler).login(
        { username : form.elements['username'].value, password : form.elements['password'].value, remember_me : form.elements['remember_me'].value}, 
        function(result) { 
            om.Clear( );
            form.elements['password'].value = '';
            form.elements['login'].disabled=false; 
            if (result * 1 != result) {                 
                alert (result);
            } else {
                userid = result;
                username = form.elements['username'].value;
                hideLoginForm();                
                showPostForm();
                eval(callback);
            }
        }
    );    
}

function hideLoginForm(){
    form = document.getElementById('loginform');
    if (form) {
        form.parentNode.removeChild(form);
    }
}

function hideLoginForm2(){
    form = document.getElementById('loginform2');
    if (form) {
        form.parentNode.removeChild(form);
    }
}

function login(parentid, callback, form_caption){
    parentdiv = document.getElementById(parentid);
    
    hideLoginForm();
        
    form = document.createElement("form"); 
    form.id = "loginform"; form.name = 'loginForm';
    form.className = "clearfix";
    parentdiv.appendChild(form);
    
    form.onsubmit = function () { 
        submitLoginForm(this, callback); return false;
    };
    
    dd = document.createElement("div");     
    dd.innerHTML = '<b>'+form_caption+' (<a href="login.php?forgotten_pass" target="_blank">'+JS_FORGOT_PASS+'</a>)</b><br /><br />';
    
    form.appendChild(dd);

    eltable = document.createElement("table"); form.appendChild(eltable);
    //eltable.style.width='100%'; 
    eltable.cellpadding="0"; eltable.cellspacing="0";
           
    tbody = document.createElement("TBODY"); eltable.appendChild(tbody);       
    
    /*    
    oRow = document.createElement("tr");tbody.appendChild(oRow); 
    oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.style.width='50%'; oCell.innerHTML = '<label><strong>'+JS_USERNAME+': </strong></label>';
    oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.style.width='50%'; oCell.innerHTML = '<label><strong>'+JS_PASSWORD+': </strong></label>';
    oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.style.width='50%'; oCell.innerHTML = '';
    */
    
    oRow = document.createElement("tr");tbody.appendChild(oRow); 
    oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.innerHTML = '<label><strong>'+JS_USERNAME+': </strong></label><input name="username" type="text" value="" />';
    oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.innerHTML = '<label><strong>'+JS_PASSWORD+': </strong></label><input name="password" type="password" value="" />';
    oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.innerHTML = '<input class="button" type="submit" name="login" value="'+JS_LOGIN+'" />';
    
    oRow = document.createElement("tr");tbody.appendChild(oRow);
    if (typeof document.all == 'undefined' || window.opera) { // NOT IE
            oCell = document.createElement("td");
            oCell.colspan = 2;oCell.setAttribute('colspan', '2');
            oCell2 = document.createElement("td");
            //oCell2.colspan = 2;oCell2.setAttribute('colspan', '2');
    } else { // IE
            oCell = document.createElement('<td colspan="2"></td>');
            oCell2 = document.createElement('<td></td>');
    }
    
    oRow.appendChild(oCell);
    oCell.innerHTML = '<input type="checkbox" id="remember_me" name="remember_me" /><label for="remember_me"><strong>'+JS_REMEMBER_ME+'</strong></label>';
    
    oCell2.innerHTML = '';    
    oRow.appendChild(oCell2);
    
    span = document.createElement("span");    
    form.appendChild(span);
        
    parentdiv.style.display='block';    
}

function hideResponseTo(response_to, comment_id) {
    bq = document.getElementById('bq_'+comment_id+'_'+response_to);
    if (!bq) return false; //already exist

    responseto = document.getElementById('sirtl_'+comment_id+'_'+response_to);
    if (responseto) {
        responseto.innerHTML = '( '+JS_SHOW+' )';
        responseto.onclick = function () { showResponseTo(response_to, comment_id); }
    }     
    bq.style.display = 'none';
}

function showResponseTo(response_to, comment_id){

    newbq = document.getElementById('bq_'+comment_id+'_'+response_to);
    if (newbq) {
        newbq.style.display = 'block';
        responseto = document.getElementById('sirtl_'+comment_id+'_'+response_to);
        if (responseto) {                
            responseto.innerHTML = '( '+JS_HIDE+' )';
            responseto.onclick = function () { hideResponseTo(response_to, comment_id); }
        }
        
        return true; //already exist
    }

    var om = new OverlayMessage();
    om.Set( JS_FETCHING_COMMENT );
    
    (new Articles_ActionHandler).getCommentForResponse(
        { article_id : article_id, 
          comment_id : comment_id, 
          response_to : response_to
        },
        function(result) { 
            om.Clear();
            if (result === false) {
                alert(JS_COMMENT_DELETED);
            } else {
                responseto = document.getElementById('sirtl_'+comment_id+'_'+response_to);
                if (responseto) {                
                    responseto.innerHTML = '( '+JS_HIDE+' )';
                    responseto.onclick = function () { hideResponseTo(response_to, comment_id); }
                }
                //vryshta comentara vyv html vid. ima teplate za celta.
                insertCommentForResponse(response_to, comment_id, result);
            }
        }
    );   
}

function insertCommentForResponse(response_to, comment_id, htmlResult){

    newbq = document.getElementById('bq_'+comment_id+'_'+response_to);
    if (newbq) {
        newbq.style.display = 'block';
        return true; //already exist
    }
                        
    //reply form container
    bq = document.getElementById('bq_'+comment_id);
    parentbox = bq.parentNode; 
    
    parentbox = document.getElementById('rrc_'+comment_id);
    
    newbq = document.createElement("blockquote");
    newbq.id = 'bq_'+comment_id+'_'+response_to;

    newbq.className = 'main sub comment';
    newbq.innerHTML = htmlResult;
    
    parentbox.style.display = 'block'; 
    
    if (bq.nextSibling) {
        parentbox.appendChild(newbq);
        //parentbox.insertBefore(newbq, bq.nextSibling);        
    } else {
        parentbox.appendChild(newbq);
    }
}

function reloadComments(form) {
    if (form) {
        //order = form.elements['order'].value;        
        rlimit = form.elements['ratelimit'].value;       
        mode = form.elements['mode'].value;

        if (mode == viewMode) {
            cpage = page;
        } else {   
            cpage = 1;
            page = 1;
        }
        //tuk zashtoto pravim sravnenie
        viewMode = mode;
                        
        if (form.elements['expandreplies']) {
            er = form.elements['expandreplies'].checked;
        } else {
            er = 0;
        }
        //trqbva da e nai otdolu za da e dostypna formata.
        document.getElementById('commentslist').innerHTML = '<img src="http://s1.phonearena.com/img/ajax-loading.gif" border=0> '+JS_LOADING+'...';
    } else {
        //order = orderby;
        rlimit = ratelimit;
        cpage = -1;
        er = expandReplies;
        mode = viewMode;
        var om = new OverlayMessage();
        om.Set(JS_REDIRECTING+'...');
    }
    
    (new Articles_ActionHandler).reload(
        { a : article_id, 
          p : cpage, 
          o : 'date',
          r : rlimit,
          g : paging,
          e : er,
          m : mode
        },
        function(result) { 
            if (cpage == -1) {
                lpath = location.pathname;
                location.href = result+'#newcommentform';
                if (result == lpath) {
                    location.reload(true);
                } 
            } else {
                document.getElementById('commentslist').innerHTML = result;
                jsdiv = document.getElementById('jsscripttag');                
                eval(jsdiv.innerHTML);
            }
        }
    );
}

function formAddInput(inputForm, elementName, elementValue, elementType, elementAttributes) {
    ele = document.createElement('span'); 
    ele.innerHTML = '<input type="' + elementType + '" name="' + elementName + '" value="' + elementValue + '" ' + elementAttributes + '>';
    inputForm.appendChild(ele);
    return ele;
}

function formAddTextArea(inputForm, elementName, elementValue, elementAttributes) {
    ele = document.createElement('span'); 
    ele.innerHTML = '<textarea name="' + elementName + '" '+elementAttributes+'>'+elementValue+'</textarea>';
    inputForm.appendChild(ele);
    return ele;
}

function formAddLabel(inputForm, elementValue) {
    var newElement = document.createElement("label");
    inputForm.appendChild(newElement);
    newElement.innerHTML = elementValue;
    return newElement;
}

function formAddBr(inputForm) {
    var newElement = document.createElement("br");
    inputForm.appendChild(newElement);
    return newElement;
}

function createSpamForm(comment_id){
   
    //reply form container
    bq = document.getElementById('bq_'+comment_id);
    
    div = document.createElement("div");
    div.className = 'spam_form';
    div.id = 'spam_form_'+comment_id; 
    
    bq.appendChild(div);
        
    form = document.createElement("form"); 
    div.appendChild(form);
    form.id = "spamf_"+comment_id; form.name = 'spamForm'+comment_id; 
    form.onsubmit = function () { 
        (new Mail_ActionHandler).sendMail(
             { uri : location.href, id : comment_id, module: 'article_comments'}, 
             function(result) {
                alert(JS_SPAM_REPORTED); 
                closeSpamForm(comment_id);
             }
        ); 
        return false;
    };
    
    strong = document.createElement("strong"); form.appendChild(strong); strong.className = 'label'; strong.innerHTML = JS_SPAM_TITLE;
    
    p = document.createElement("p"); form.appendChild(p); p.innerHTML = JS_SPAM_TEXT;
    
    formAddInput(form, 'close', JS_CLOSE, 'button','class="button" onclick="closeSpamForm('+comment_id+'); return false;"');                
    formAddInput(form, 'id', comment_id, 'hidden','');
    formAddInput(form, 'submit', JS_REPORT, 'submit','class="button"');

    div.style.display='block';
}

function closeSpamForm(id) {
    
    bq = document.getElementById('bq_'+id);
    div = document.getElementById('spam_form_'+id);
    bq.removeChild(div);
}

function createReplyForm(id){

    if (userid == 0 && anoncancomment == 0) {
        login('rfc_'+id, 'createReplyForm('+id+');', JS_LOGIN_WITH_UN_PASS);
        return false;
    } 
    
    //reply to comment link
    replylink = document.getElementById('rcl_'+id);
    if (replylink) replylink.style.display = 'none';
        
    editlink = document.getElementById('ecl_'+id);
    if (editlink) {
        editlink.style.display='none'; 
    }

    //reply form container
    parentdiv = document.getElementById('rfc_'+id);
    
    //da proveruim za edit form
    editform = document.getElementById('ef_'+id);
    if (editform) hideEditForm(id); 
    
    //reply form    
    form = document.getElementById('rf_'+id);    
    if (!form) {
        
        form = document.createElement("form"); 
        parentdiv.appendChild(form);
        form.id = "rf_"+id; form.name = 'replyform_'+id; form.onsubmit = function () { submitReplyForm(this); return false;};
        
        formAddInput(form, 'article_id', article_id, 'hidden','');
        formAddInput(form, 'comment_id', id, 'hidden','');
        /* formAddBr(form); */
        
        eltable = document.createElement("table"); form.appendChild(eltable);
        eltable.style.width='100%'; eltable.cellpadding="0"; eltable.cellspacing="0";
        
        tbody = document.createElement("TBODY"); eltable.appendChild(tbody);       
        
        if (userid == 0) {
            oRow = document.createElement("tr");tbody.appendChild(oRow); 
            oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.style.width='50%'; oCell.innerHTML = '<label><strong>'+JS_USERNAME+': </strong><small style="color:gray;">('+JS_OPTIONAL+')</small></label>';
            oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.style.width='50%'; oCell.innerHTML = '<label><strong>'+JS_EMAIL+': </strong><small style="color:gray;">('+JS_OPTIONAL_EMAIL+')</small></label>';

            oRow = document.createElement("tr");tbody.appendChild(oRow); 
            oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.innerHTML = '<input name="name" type="text" value="" />'; 
            oCell = document.createElement("td"); oRow.appendChild(oCell); oCell.innerHTML = '<input name="email" type="text" value="" />';            
        } else {
            formAddInput(form, 'name', username, 'hidden','');
            formAddInput(form, 'email', useremail, 'hidden','');
        }
        
        oRow = document.createElement("tr");tbody.appendChild(oRow);
        if (typeof document.all == 'undefined' || window.opera) { // NOT IE
            oCell = document.createElement("td");
            oCell.colspan = 2;oCell.setAttribute('colspan', '2');
            oCell2 = document.createElement("td");
            oCell2.colspan = 2;oCell2.setAttribute('colspan', '2');
        } else { // IE
            oCell = document.createElement('<td colspan="2"></td>');
            oCell2 = document.createElement('<td colspan="2"></td>');
        }
        
        oRow.appendChild(oCell);

        if (webkbd_enabled) {
            webkbdhtml = '<a class="webkbd-switcher" style="float: right; font-family: arial,helvetica,clean,sans-serif; font-weight: bold; cursor: pointer; padding: 2px 8px; border: 1px solid black; text-decoration: none;" href="http://code.ppetrov.com/webkbd/" onclick="return webkbd.switcherClicked(event);"></a>';
        } else {
            webkbdhtml = '';
        }
        
        oCell.innerHTML = '<label><strong>'+JS_ADD_COMMENT+': </strong></label>'+webkbdhtml;
        
        //init webkbd if enabled
        if(webkbd_enabled && webkbd) webkbd.init();
        
        var tempWidth = '99.5%';
        var tempHeight = '100px';
        var tempId = 'contentrb_'+id;
        var tempAction = '/_libs/activespell/spell_checker.php?l='+JS_SPELL_LANG;

        oRow = document.createElement("tr");tbody.appendChild(oRow);
        oRow.appendChild(oCell2);        
        oCell2.innerHTML = '<textarea rows="4" cols=100 style="width: '+tempWidth+'; height: '+tempHeight+';" id="'+tempId+'" name="content" accesskey="' + tempAction + '"></textarea>';
        
        //formAddTextArea(form, 'content', '', 'id="'+tempId+'" style="width: '+tempWidth+'; height: '+tempHeight+';" rows="4" cols="80"');
        formAddBr(form);
        
        elul = document.createElement("UL"); form.appendChild(elul);
        elul.className = 'buttons';
        
        if (userid > 0) {
            pstr = '<li style="float:left;">'+TEXT_COMMENT_EDITABLE+'</li>';
        } else if (userid == 0) {
            pstr = '<li style="float:left;">'+TEXT_PLEASE_LOGIN_REGISTER+'</li>';
        }
                
        elul.innerHTML = pstr+'<li><input class="button" name="cancel" type="button" value="'+JS_CANCEL+'" /></li><li><input class="button" id="srb_'+id+'" type="submit" name="submit" value="'+JS_POST_REPLY+'" /></li>';
                                        
        form.elements['cancel'].onclick = function () { hideReplyForm(id); return false;};
        
        span = document.createElement("span"); parentdiv.appendChild(span);
        span.className = 'clearfix';
                
        //start spelling        
        eval('spellCheckers' + id + '= new activeSpell("spellCheckers' + id + '", tempWidth, tempHeight, tempAction, "spellCheckDiv' + id + '", "content", tempId, "spellcheck", "");');        
    }

    parentdiv.style.display='block';
}

function hideReplyForm(id){
    //reply form container
    parentdiv = document.getElementById('rfc_'+id);
    parentdiv.style.display = 'none';
  
    form = document.getElementById('rf_'+id);
    if (form) {
        form.elements['content'].value = '';        
    }

    //reply to comment link
    replylink = document.getElementById('rcl_'+id);
    if (replylink) replylink.style.display = 'inline';

    
    editlink = document.getElementById('ecl_'+id);
    if (editlink) {
       editlink.style.display='none'; 
    }
}

function submitReplyForm(form) {
    //reply form
    //form = document.getElementById('rf_'+id);
    
    if (!form) {
        alert(JS_ERR_ONPOSTING);
        return false;
    }
       
    if (form.elements['content'].value.length ==0 || form.elements['content'].value.trim().length == 0) {
        alert(JS_ERR_ENTER_COMMENT); 
        return false;
    }

    form.elements['submit'].disabled=true;
       
    var om = new OverlayMessage();
    om.Set( JS_SAVING_REPLY+'...' );
    
    (new Articles_ActionHandler).postComment(
        { name : form.elements['name'].value, email : form.elements['email'].value, content : form.elements['content'].value, article_id : form.elements['article_id'].value, comment_id : form.elements['comment_id'].value }
        , 
        function(result) { 
            form.elements['submit'].disabled=false; 
            om.Clear();
            
            if (result === false) {
                alert (JS_ERR_GENERAL_ONPOST); 
            } else if (result === -1) {
                alert (JS_ERR_VALID_EMAIL); 
            } else if (result === -2) {
                alert (JS_ERR_ENTER_COMMENT);                 
            } else if (result === -3) {
                alert (JS_ERR_UN_OCCUPIED);                 
            } else if (result === -4) {
                alert (JS_ERR_EMAIL_OCCUPIED);                 
            } else if (result === -5) {                
                alert(JS_ERR_ONE_COMMENT);
            } else { 
                if (viewMode == 0) {
                    //threaded mode, reload replies
                    id = form.elements['comment_id'].value; 
                    hideReplyForm(id);                        
                    showreplies(result);
                } else {
                    //response
                    reloadComments();
                }
            }            
        }
    );
}

function fixRepliesCount(id, replies) {
    var srcl = document.getElementById('srcl_'+id);
    if (srcl) {
     srcl.innerHTML = replies;
    }
    var srbcl = document.getElementById('srbcl_'+id);
    if (srbcl) {
        srbcl.innerHTML = replies;
    }
}

function submitComment(form) {
    //reply form
    
    if (!form) {
        alert(JS_ERR_ONPOSTING);
        return false;
    }

    if (form.elements['content'].value.trim().length == 0) {
        alert(JS_ERR_ENTER_COMMENT); 
        return false;
    }

    form.elements['submit'].disabled=true; 
        
    var om = new OverlayMessage( );
    om.Set( JS_SAVING_COMMENT+'...' );
    
    (new Articles_ActionHandler).postComment(
        { name : form.elements['name'].value, title : form.elements['title'].value, email : form.elements['email'].value, content : form.elements['content'].value, article_id : form.elements['article_id'].value }, 
        function(result) { 
            om.Clear( );
            form.elements['submit'].disabled=false; 
            if (result === true) { 
                reloadComments();
            } else {            
	            if (result === false) {
                    alert (JS_ERR_GENERAL_ONPOST); 
        	    } else if (result === -1) {
                    alert (JS_ERR_VALID_EMAIL); 
        	    } else if (result === -2) {
                    alert (JS_ERR_ENTER_COMMENT);                 
        	    } else if (result === -3) {
                    alert (JS_ERR_UN_OCCUPIED);                 
        	    } else if (result === -4) {
                    alert (JS_ERR_EMAIL_OCCUPIED);                
        	    } else if (result === -5) {                
                    alert(JS_ERR_ONE_COMMENT);
	                //alert (result || 'Sorry, your comment could not be processed at this time.');                
                }            
            }
        }
    );
}

function editComment(id){
    
    //reply to comment link
    replylink = document.getElementById('rcl_'+id);
    if (replylink) replylink.style.display = 'none';
    
    editlink = document.getElementById('ecl_'+id);
    if (editlink) editlink.style.display='none'; 

    //comment content
    cc = document.getElementById('cc_'+id);
    parentdiv = document.getElementById('rfc_'+id);
    
    form = document.createElement("form"); 
    form.id = "ef_"+id; form.name = 'editform_'+id; form.onsubmit = function () { submitEditForm(this); return false;};
    formAddInput(form, 'article_id', article_id, 'hidden','');
    formAddInput(form, 'comment_id', id, 'hidden','');           
 
    eltable = document.createElement("table"); form.appendChild(eltable);
    eltable.style.width='100%'; eltable.cellpadding="0"; eltable.cellspacing="0";
        
    tbody = document.createElement("TBODY"); eltable.appendChild(tbody);

    var tempWidth = '99.5%';
    var tempHeight = '100px';
    var tempId = 'contentrb_'+id;
    var tempAction = '/_libs/activespell/spell_checker.php?l='+JS_SPELL_LANG;
    var tempContent = cc.innerHTML; 
    
    oRow = tbody.insertRow(-1);
    oCell = oRow.insertCell(-1); oCell.setAttribute('colspan', '2');
    oCell.innerHTML = '<textarea rows="4" style="width: '+tempWidth+'; height: '+tempHeight+';" id="'+tempId+'" name="content" accesskey="'+tempAction+'">'+tempContent+'</textarea>';
    
    /*formAddTextArea(form, 'content', tempContent, 'id="'+tempId+'" style="width: '+tempWidth+'; height: '+tempHeight+';" rows="6" cols="80"');*/
    
    elul = document.createElement("UL"); form.appendChild(elul);
    elul.className = 'buttons';        
    elul.innerHTML = '<li><input class="button" name="cancel" type="button" value="'+JS_CANCEL+'" /></li><li><input class="button" id="srb_'+id+'" type="submit" name="submit" value="'+JS_SAVE+'" /></li>';
    form.elements['cancel'].onclick = function () { hideEditForm(id); return false;};

    /*formAddInput(form, 'submit', 'post', 'submit','id="srb_'+id+'" class="button"');
    formAddInput(form, 'cancel', 'cancel', 'button','class="button" onclick="hideEditForm('+id+'); return false;"');*/
    
    cc.style.display = 'none';
    //cc.appendChild(form);
    parentdiv.innerHTML = '';
    parentdiv.appendChild(form);
    
    span = document.createElement("span"); parentdiv.appendChild(span); 
    span.className = 'clearfix';
    
    parentdiv.style.display = 'block';
    
    //start spelling    
    eval('spellCheckers' + id + '= new activeSpell("spellCheckers' + id + '", tempWidth, tempHeight, tempAction, "spellCheckDiv' + id + '", "content", tempId, "spellcheck", tempContent);');
    
}

function hideEditForm(id){
    //reply comment link
    /*
    replylink = document.getElementById('rcl_'+id);
    if (replylink) replylink.style.display = 'inline';
    */
    
    editlink = document.getElementById('ecl_'+id);
    if (editlink) {
       editlink.style.display='inline'; 
    }    
        
    parentdiv = document.getElementById('rfc_'+id);
    if (parentdiv) {
        parentdiv.innerHTML = '';
        parentdiv.style.display = 'none';
    }
    
    //comment content
    cc = document.getElementById('cc_'+id);
    cc.style.display = 'block';    
}

function submitEditForm(form) {

    if (!form) {
        alert(JS_ERR_ONPOSTING);
        return false;
    }
        
    if (form.elements['content'].value.trim().length == 0) {
        alert(JS_ERR_ENTER_COMMENT); 
        return false;
    }


    var om = new OverlayMessage( );
    om.Set( JS_SAVING_REPLY+'...' );

    form.elements['submit'].disabled = true;
    form.style.display='none';
    
    (new Articles_ActionHandler).editComment(
        { content : form.elements['content'].value, article_id : form.elements['article_id'].value, comment_id : form.elements['comment_id'].value }
        , 
        function(result) { 
            om.Clear(); 
            form.elements['submit'].disabled = false;        
            form.style.display='block';
            if (result !== false) {
                id = form.elements['comment_id'].value;
                cc = document.getElementById('cc_'+id);
                cc.innerHTML = result.replace(/\n/g, "<br />");;               
                cc.style.display = 'block';
                
                parentdiv = document.getElementById('rfc_'+id);
                parentdiv.innerHTML = '';
                parentdiv.style.display = 'none';
                                
                editlink = document.getElementById('ecl_'+id);
                if (editlink) editlink.style.display='inline'; 

                counter = document.getElementById('ecc_'+id);    
                var timeLeft = Number(counter.innerHTML);
                
                if (timeLeft > 0) {
                    makeCommentEditable(id, timeLeft);
                }                                
            } else {
                alert (JS_ERR_GENERAL_ONPOST); 
                return false;
            }
        }
    );
}

function makeCommentEditable(id, timeLeft) {
    
    if (typeof timeLeft == 'undefined') {
        timeLeft = edittimeout;
    }
    
    box = document.getElementById('cb_'+id);
    cc = document.getElementById('cc_'+id);
        
    editspan = document.getElementById('es_'+id);
    if (editspan) return false; 
    
    editspan = document.createElement('span');
    editspan.id = 'es_'+id; 
    
    box.insertBefore(editspan, cc); 
    
    link = document.createElement('a'); 
    link.id = 'ecl_'+id;
    link.href="javascript:;"; 
    link.onclick = function () {
        editComment(id); 
        return false;
    }
    link.innerHTML = JS_CLICK_TO_EDIT;
    editspan.appendChild(link);
    editspan.className = 'replies';
    
    span = document.createElement('span');  span.innerHTML = ' &nbsp;('+JS_AVAILABLE_FOR+'&nbsp;'; editspan.appendChild(span, cc);
    span = document.createElement('span');  span.id = 'ecc_'+id; span.innerHTML = timeLeft; editspan.appendChild(span, cc);
    span = document.createElement('span');  span.innerHTML = '&nbsp;'+JS_SECONDS+')'; editspan.appendChild(span, cc);    
}

function editTimeout(id) {

    counter = document.getElementById('ecc_'+id);    
    if (!counter) return false;
    var count = Number(counter.innerHTML);
    
    if (count <= 0) {
        stopEdit(id);
    }

    counter.innerHTML = count -1;
};

function startEdit(id) {
   if (edits[id] == undefined) {
      edits[id] = setInterval("editTimeout('" + id + "')", 1000);
   }
}
    
function stopEdit(id) {
   clearInterval(edits[id]);
   
   editspan = document.getElementById('es_'+id);    
   if (editspan) {
        box = document.getElementById('cb_'+id);   
        box.removeChild(editspan);
   }
   
   editform = document.getElementById('ef_'+id); 
   if (editform) {
        hideEditForm(id);
   }
}

function showreplies(id){
    //replies container    
    repliesc = document.getElementById('replies_'+id);
    if (!repliesc) return false;
    
    repliesc.innerHTML = '<img src="http://s1.phonearena.com/img/ajax-loading.gif" border=0>';
    repliesc.style.display = 'block'; 
    
    
    (new Articles_ActionHandler).showReplies({ comment_id  : id}, 
    function(result) { 
        if (result === false) { 
            alert (JS_ERROR_GETTING_REPLIES); 
        } else { 
            el = document.getElementById('replies_'+id); 
            if (el) {
                el.innerHTML = result;
                
                //jsdiv = document.getElementById('jsscripttag');
                //eval(jsdiv.innerHTML);
                
                var my_array = el.getElementsByTagName("script");
                var i; var j;
                for (i=0,j=0;i<my_array.length;i++) {
                    jsdiv = my_array[i];
                    eval(jsdiv.innerHTML);
                }
                
            }
            
            document.getElementById('rbtb_'+id).style.display = 'block';
            document.getElementById('rtb_'+id).style.display = 'none';
            ln = document.getElementById('rcl_'+id);
            if (ln) {
                ln.style.display = 'none';
            }
            ln = document.getElementById('srl_'+id);
            if (ln) {
                ln.style.display = 'none';
            }
        } 
    }
    );    
}

function hidereplies(id){

    repliesc = document.getElementById('replies_'+id);
    if (repliesc) {
        repliesc.style.display = 'none';
        repliesc.innerHTML = '';
    }
    
    document.getElementById('rbtb_'+id).style.display = 'none';
    document.getElementById('rtb_'+id).style.display = 'block';

    ln = document.getElementById('rcl_'+id);
    if (ln) {
        ln.style.display = 'block';
    }
    ln = document.getElementById('srl_'+id);
    if (ln) {
        ln.style.display = 'block';
    }
    
}

function showcomment(id){
    //comment box
    document.getElementById('cb_'+id).style.display = 'block';
    var link = document.getElementById('scl_'+id);    
    if (link) {
        link.onclick = function() {hidecomment(id)};
        //link.title = 'Hide';
        link.innerHTML = JS_HIDE;    
    }
    return false;
}

function hidecomment(id){
    var link = document.getElementById('scl_'+id);
    if (link) {
        link.onclick = function() {showcomment(id)};
        //link.title = 'Show';
        link.innerHTML = JS_SHOW;
    }
    document.getElementById('cb_'+id).style.display = 'none';
}

function vote(id, vote) {

    if (userid == 0) {
        login('rfc_'+id, 'vote('+id+', '+vote+');', JS_VOTE_LOGIN_WITH_UN_PASS);
        return false;
    }

    var om = new OverlayMessage();
    om.Set( JS_SENDING_VOTE+'...' );

    (new Articles_ActionHandler).vote({ comment_id  : id, voted: vote, article_id : article_id}, 
    function(result) { 
        om.Clear();
        if (result !== true) { 
            alert (result || JS_ERROR_ON_VOTING); 
        } else {
            //rating        
            cr = document.getElementById('vr_'+id);            
            newrating = (parseInt(cr.innerHTML) + parseInt(vote));
            
            if (newrating > 0) {
                cr.style.color='green';
                newrating = '+'+newrating;
            } else if (newrating < 0) {
                cr.style.color='red';
            } else {
                cr.style.color='#575757';
            }

            cr.innerHTML = newrating; 
            
            vl = document.getElementById('vl_'+id);
            vl.innerHTML = '<a class="addToolTip" title="<p>'+JS_ERR_ALREADY_VOTED+'</p>" href="javascript:;" onclick="alert(\''+JS_ERR_ALREADY_VOTED+'\');"><img src="http://s1.phonearena.com/img/icon_pos_gray_2.gif" alt="" /></a><a class="addToolTip" title="<p>'+JS_ERR_ALREADY_VOTED+'</p>" href="javascript:;" onclick="alert(\''+JS_ERR_ALREADY_VOTED+'\');"><img src="http://s1.phonearena.com/img/icon_neg_gray_2.gif" alt="" /></a><a class="addToolTip" title="<p>'+JS_REPORT_SPAM+'</p>" href="javascript:;" onclick="createSpamForm('+id+'); return false;"><img src="http://s1.phonearena.com/img/icon_att_gray.gif" onmouseover="this.src=\'http://s1.phonearena.com/img/icon_att.gif\'" onmouseout="this.src=\'http://s1.phonearena.com/img/icon_att_gray.gif\'" alt="'+JS_REPORT_SPAM+'" /></a>';
            eval('if (window.addwarning) addwarning();');
            //if (vote == -1) hidecomment(id);
        } 
    });
}

function deleteComment(id) {

    var answer = confirm("Delete comment?")
    
    if (answer){

        var om = new OverlayMessage();
        om.Set( 'Deleting...' );

        (new Articles_ActionHandler).deleteComment({ comment_id  : id}, 
        function(result) {
            om.Clear();
            if (result !== true) { 
                alert (result); 
            } else {
                //rating        
                bq = document.getElementById('bq_'+id);
                if (bq) {
                    bq.style.display = 'none';
                }
                //alert('Deleted');
            }
        });
    }
}

var edits = new Array();

OverlayMessage = function ( )
    {    
    this.overlay = document.createElement( 'div' );
    document.body.appendChild( this.overlay );
    h = parseInt(document.body.parentNode.scrollTop + (screen.height /2)) - 200;
    this.visibleStyle = 'position: absolute; top: '+h+'px; left: 50%; color: #FFFFFF; font-weight: bold; background-color: ' + OverlayMessage.backgroundColor + '; width: 200px; text-align: center; margin-left: auto; margin-right: auto; padding: 20px; border: 1px solid ' + OverlayMessage.borderColor + '; z-index: 2000; opacity: .90; filter: alpha(opacity=90);';
  
    this.invisibleStyle = 'display: none;';
    this.overlay.style.cssText = this.invisibleStyle;
};


OverlayMessage.backgroundColor = '#3A80B1';
OverlayMessage.borderColor = '#5292BF';


OverlayMessage.prototype.Set = function ( message )
    {
    this.overlay.innerHTML = message;
    this.overlay.style.cssText = this.visibleStyle;
    };


OverlayMessage.prototype.Clear = function ()
    {
    this.overlay.style.cssText = this.invisibleStyle;
    };


OverlayMessage.SetBackgroundColor = function ( color )
    {
    OverlayMessage.backgroundColor = color;
    };


OverlayMessage.SetBorderColor = function ( color )
    {
    OverlayMessage.borderColor = color;
    };


var EmptyFunction = function() {};

function Class (t, base) {
	var q = function() {
		this.__construct.apply (this, arguments);
	};
	
	if (base) {
		for (var property in base.prototype) {
			q.prototype[property] = base.prototype[property];
		}
		q.prototype.__parent = base;
	}
	
	for (var property in t) {
		q.prototype[property] = t[property];
	}

	q.prototype.__callParent = function() {
		return this.__parent.prototype[arguments[0]].apply (this, 
			Array.prototype.slice.apply (arguments, [1]));
	}
	
	q.prototype.__callBase = function() {
		var obj = eval ("window." + arguments[0]);
		return obj.prototype[arguments[1]].apply (this, 
			Array.prototype.slice.apply (arguments, [2]));
	}
	
	if (!q.prototype.__construct) q.prototype.__construct = EmptyFunction;
	return q;
}

//Compat fuction for IE.

if (!window.XMLHttpRequest) {
	window.XMLHttpRequest = function() {

		var xmlVersions = [ "MSXML2.XMLHttp.5.0",
			"MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0",
			"MSXML2.XMLHttp","Microsoft.XMLHttp"
		];
	
		for (var i = 0; i < xmlVersions.length; i++) {
			try {
				var x = new ActiveXObject(xmlVersions[i]);
				return x;
			} catch (e) {}
		}
	};
	
}

var Ajax = {
	request : function (server, data, callback, method) {
		var request = new Ajax.Request (server, data, callback, method);
		request.doRequest();
	},

	Request : new Class ({

		__construct : function (server, data, callback, method) {
			this.method = method || 'POST';
			this.requestObject = new XMLHttpRequest();
			this.callback = callback;
			this.data = data;
			this.server = server;
		},
		
		onReadyStateChange : function (event) {
		    // only if req shows "loaded"
	    	if (this.requestObject.readyState == 4) {

//AjaxLoader.stop();


	        	// only if "OK"
	        	if (this.requestObject.status == 200) {
	        		this.callback (this.requestObject.responseText, this.requestObject.responseXML);
	        	} else {
	        		throw new Error ("Server returned status " + this.requestObject.status);
	        	}
	        } else {
	            /*throw new Error ("There was a problem retrieving the XML data:\n" + requestObject.statusText);*/
	        }
		},
		
		doRequest : function() {
//AjaxLoader.start();


			this.requestObject.open (this.method, this.server, true);
			this.requestObject.setRequestHeader ("Content-Language", "en");
			this.requestObject.setRequestHeader ("Content-Charset", "utf-8");
			this.requestObject.setRequestHeader ("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
			this.requestObject.setRequestHeader("Accept-Charset", "utf-8"); 
			var thisref = this;
			this.requestObject.onreadystatechange = function (e) {
				thisref.onReadyStateChange.call (thisref, e);
			};
			this.requestObject.send (this.data);
		}
		
	})
	
};	

var Iframe = {

    iframe : undefined,

    server : 'iframe.php',

    submit : function (form, callback) {
        var ifr = new Iframe.Fallback (Iframe.server, form, callback);
        ifr.submit();
    }
    
};
/*
Iframe.Fallback = new Class ({

    __construct : function (server, form, callback) {
        this.server = server;
        this.form = form;
        this.callback = callback;
        if (Iframe.iframe === undefined) {
        	Iframe.iframe = document.getElementById ('iframe');
            //Iframe.iframe = document.createElement ('IFRAME');
            //Iframe.iframe.className = 'upload'; 
            //Iframe.iframe.src = "javascript:;";
            //Iframe.iframe.id = Iframe.iframe.name = 'iframe';
            //document.body.appendChild (Iframe.iframe);
        }
    },
    
    submit : function() {
        this.oldAction = this.form.action;
        this.oldTarget = this.form.target;
        this.form.action = this.server;
        this.form.target = Iframe.iframe.id;
        this.form.submit();
        var thisref = this;
        this.interval = setInterval (function() {
            if (Iframe.iframe.contentWindow && Iframe.iframe.contentWindow.isLoaded) {
                Iframe.iframe.contentWindow.isLoaded = false;
                clearInterval (thisref.interval);
                thisref.form.action = thisref.oldAction;
                thisref.form.target = thisref.oldTarget;
                
                thisref.callback (Iframe.iframe.contentWindow.randomId);
                //delete thisref.interval;              
            }
        }, 100);
    }

});
*/
var Iframe = {
    iframe : undefined,
    submit : function (callback) {
        var ifr = new Iframe.Fallback (callback);
        ifr.submit();
    },
	Fallback : new Class ({
	    __construct : function (callback) {
	        this.callback = callback;
	        if (Iframe.iframe === undefined) {
	        	Iframe.iframe = document.getElementById ('iframe');
	        }
	    },
	    submit : function() {
	        document.forms[0].submit();
	        this.interval = setInterval (Delegate (this, function() {
	            if (Iframe.iframe.contentWindow && Iframe.iframe.contentWindow.isLoaded) {
	                Iframe.iframe.contentWindow.isLoaded = false;
	                clearInterval (this.interval);
	                this.callback (Iframe.iframe.contentWindow.randomId, Iframe.iframe.contentWindow.filesCount);
	                //delete thisref.interval;              
	            }
	        }), 100);
	    }
	})
};

var JSON = {

	server : 'json.php',
	
	errorHandler : function (error) {
	    //$('debug_error').innerHTML = error + 
	        //'<button onclick="this.parentNode.style.display = \'none\'">Close</button>';
	    //$('debug_error').style.display = 'block';
		alert (error);
	},
	
    instance : function (server) {
        if (server) this.server = server;
    },
    
    encode : function (data) {
    	var encoder = new JSON.encoder (data, 25);
    	return encoder.encode (data);
    },
        
	callPhpFunction : function (func, params, callback) {
		var json = new JSON.request (this.server, callback, func, params);
    	json.doRequest();
	},
    
    callClass : function (className, method, params, callback) {
    	var json = new JSON.request (JSON.server, callback, [className, method], params);
    	json.doRequest();
    }
    
}
		
JSON.request = new Class ({		
	
	__construct : function (server, callback, func, params) {
		this.server = server;		
		this.callback = callback;
		this.func = func;
		this.params = params;
	},
		
	doRequest : function() {
		var query = JSON.encode ({ 'method' : this.func, 'params' : this.params, 'id' : 1 });

		var thisref = this;
		Ajax.request (this.server, query, function (data) {
			var callOk, callErr;

			if (thisref.callback instanceof Array) {
				callOk = thisref.callback[0];
				callErr = thisref.callback[1];
			} else {
				callOk = thisref.callback;
				callErr = JSON.errorHandler;
			}
			
			try {
			    data = eval ("(" + data + ")");
			} catch (e) {
			    callErr ("Invalid response: " + data);
			    return;
			}
			if (data.error == null) {
				callOk (data.result);
			} else {
				callErr (data.error);
			}
		});
	},
	
	call : function (callback, func) {
		var extra = '';
		for (var i = 2; i < arguments.length; i++) extra += ", arguments[" + i + "]";
		return eval ("this.callServer_old (this.server, callback, func" + extra + ");");
	}
			
});

JSON.encoder = new Class ({
	
	__construct : function (source, maxDepth) {
		this.source = source;
		this.maxDepth = maxDepth || 25;
		this.depth = 0;
		this.pieces = [];
		this.piecesLength = 0;
	},
	
	escapeSymbols : {
		'\b' : '\\b',
		'\f' : '\\f',
		'\n' : '\\n',
		'\r' : '\\r',
		'\t' : '\\t'
	},
		
	addPiece : function (piece) {
		this.pieces[this.piecesLength++] = piece;
	},
				
	parseSource : function (cVar) {
		this.depth++;
		if (this.depth > this.maxDepth) return this.addPiece ('null');
		
		switch (typeof cVar) {
	        case 'boolean': return this.addPiece (String (cVar));
	        case 'number': return this.addPiece (isFinite (cVar) ? +cVar : 'null');
	        case 'string':
	        	var len = cVar.length;
	        	this.addPiece ('"');
	        	for (var i = 0; i < len; i++) {
	        		var ch = cVar.charAt (i);
	        		if (ch < ' ') {
	        			var escSymbol = this.escapeSymbols [ch];
	        			if (escSymbol) {
	        				this.addPiece (escSymbol);
	        			} else {
	        				ch = ch.charCodeAt();
	                        this.addPiece (
	                        	'\\u00' + 
	                        	Math.floor(ch / 16).toString(16) +
	                            (ch % 16).toString(16)
	                        );
	        			}
	        		} else {
	                    if (ch == '\\' || ch == '"') this.addPiece ("\\");
	                    this.addPiece (ch);
	        		}
	        	}
	        	return this.addPiece ('"');
	        case 'object':
	        	if (!cVar) return this.addPiece ('null');
	        	if (cVar instanceof Array) {
	        		var len = cVar.length;
	        		this.addPiece ('[');
	        		for (var i = 0; i < len; i++) {
	        			if (i) this.addPiece (',');
	        			this.parseSource (cVar[i]);
	        			this.depth--;
	        		}
	        		return this.addPiece (']');
	        	}
	        	if (typeof cVar.toString == 'undefined') return this.addPiece ('null');
	        	this.addPiece ('{');
				var isFirst = true;
				for (var i in cVar) {
					var obj = cVar[i];
					var oType = typeof obj;
					if (/*cVar.hasOwnProperty (obj) &&*/ oType != 'undefined' && oType != 'function') {
						if (isFirst) isFirst = false; else this.addPiece (',');
						this.parseSource (i);
	        			this.depth--;
						this.addPiece (':');
						this.parseSource (obj);
	        			this.depth--;
					}
				}
	        	return this.addPiece ('}');
	        	
		}
		return this.addPiece ('null');
	},
	
	encode : function() {		
		this.parseSource (this.source);
		return this.pieces.join ('');
	}

});