

var hbl_hostname="hab.la/rpc";
var hbl_static_hostname="static.hab.la/js";
/* The games begin here */

var hbl = {
  'util'         : {},
  'pipelines'    : {},
  'themes'       : {},
  'hwindow_util' : {},
  'plugins'      : {},
  
  /* backwards compatible */
  'hwindow'      : {
    'config' : function() {
      this.vars = {};
      this.palette = {};
      this.style_id = {};
      this.style = {};
      this.style_classes = {};
      this.style_classes_map = {};
    }
    
  },
  jsoncallback_norv : function() {} // an artifact
};
/* Now the functions that I am going to actually use. */
hbl.util.eventmanager = function(){
  this.registered_events = {};
  
  // Register a handler
  this.register = function(event_name, handler, priority){
    if(this.registered_events[event_name] == undefined ) {
      this.registered_events[event_name] = new Array();
    }
    if(priority == undefined) {
      priority  = 999;
    }
    
    this.registered_events[event_name].push({"handler" : handler, "priority" : priority });
    this.registered_events[event_name] = this.registered_events[event_name].sort( function(a,b) { return (a["priority"] - b["priority"]); });
  };
  
  // Handler
  this.handle = function(event_name, arg) {
    if(this.registered_events[event_name] == undefined) return;
    
    if(arg == undefined) {
      arg = {};
    }
    arg["window"]     = this.win;
    arg["event_name"] = event_name;
    hbl.util.debug("handling:" + event_name);
    
    var buf = "";
    for( var ii in arg) {
      buf += ii + "=" + arg[ii] + "|";
    }
    hbl.util.debug("args" + buf);
    
      
    for (var i in this.registered_events[event_name]) {
      if( this.registered_events[event_name][i] && typeof(this.registered_events[event_name][i]["handler"]) == "function") {
        //hbl.util.debug("fired event" + this.registered_events[event_name][i]["handler"].toString() );
        this.registered_events[event_name][i]["handler"](arg);
      }
    }
  }; // end handle

  // Cleans off events for an event handle
  this.unregister_all = function(event_name) {
    this.registered_events[event_name] = [];
  };

  this.setWindow = function(win) {
    this.win = win;
  };
};
/* define the event manager ------------------------------------------------------------------------------- */




// will be replaced later
//var hbl_hostname = "hab.la/rpc";
//var hbl_static_hostname = "static.hab.la/js";


var hblrpcTrick = 1;
var hblDEBUG = 0;
var hblnoconsole= 1;
var hblHideUnsupported = 1;
var hblJavaScriptVersion = "0.5j";

var hblBegincall = "begin_2";



/* set the basic ENV */
hbl.eventmgr = new hbl.util.eventmanager();
habla_window = undefined;




/*
Copyright, Hab.la Group (Ben Congleton & Kevin Ferguson 2007) http://www.hab.la 

Please do not copy to your server, because we are constantly updating how the javascript works,
and we don't want Hab.la to break.

---------------------
Looking for an internship?  Can you hack javascript?  Does this code make sense to you?
send us a message through: http://www.hab.la/survey/show/1 include your email.  We'll
get back to you ASAP.

Javascript recruiting..(c) :-)

--------------------


  TODO: 
  JS|
    * Remap...  palette and change defaults.
    * 
    ---
                     
    Test more
    See if it compresses nicely
    
  Python|
     * better storage mechanism for dconfig and config
     (also some method for setting these variables in RUBY) 
        -- could almost just encode JSON and store it along with the site?
        --- [not a prob at all :-)]
        -- can I pickle using Ruby?

        wc_default_config  - JSON
        wc_over_write_config   - JSON
        settings    - YAML
        
        // stats
        times_detected    - (times online) INT
        last_detected     - timestamp
        
        begin_call_detected - (boolean)
          (more stats are in the logging table)
        

*/




/*
 I was thinking about it.. and most of this dynamic loading stuff doesn't actually make that much sense,
 because.. A..  99% of the users will load everything, and
 I should just preload everything for them.
 
 So really.. the only things I need to dynamically load are
 
 PLUGINs and THEMES

DONE AND DONE:
Plugins + Plugin Stubs (check)
Dynamic Plugin Loading.... (check)
  * it PROBABLY doesn't matter if the plugins are completely loaded when we initialize the theme
  * (as far as I can tell)
  * should FIRE, After theme is loaded!
    - Need a good method of wrapping all the files into ONE Javascript
       Test basic init
  - need a config option for NOT appending the DIV to the body.  (for YUI.. etc..)  (vars["not_append"])
  
  Better Style --> engine
               --> [change how style is rendered, maybe so it's more ElementID oriented? ]
               --> Probably Rewrite Theme.js so that the HABLA_ID matches
                   the internal name.
                   which will also make it easier to style.
  explore base-64 encoding 
 */

/*
 * This is a stub loader 
 * it does VERY little
 * but that is all it is designed to do.
 *
 * It merely help's reduce the file size of content included into a page.
 * and does this for the best.
 *
 * Essentially, it builds a list that looks like this
 *
 * status [ file => 1, file2 => 0 ]
 * 
 * when all files are done loading it fires an event.
 *
 * for backwards compatibility, it defines a CONFIG object (that's empty)
 * and wc_init.
 *
 * What does a Barebones thing need:
 *
 *
 * I need at least the following objects
 *
 * Config
 * PluginList
 * EventManager
 * LoadingJavascript
 *
 */
 










/*
  Goal for tommorrow;
  
  finish splitting up code into seperate files.
  add testing?
  
  think about how config and about how theme - ing is going to work
  (pull theme stuff out of the hab.la window object)
  
  clean up client code more.
     HBLID cookie settings.. and passing HABLASESSION as URL param (with RPC server help)

  on backend
    split up session information, into "memcache / memory/ and SQL"
 */

/* ok, that's it for the STUB loader -- everything else just does:

// Hopefully this split will make it EASIER to 
// edit individual files, etc.


*/


/* -----------------------------------------------------------------------------------------------------------------------------*/

/* The key config object */
// This is the CORE of a config object


/* -----------------------------------------------------------------------------------------------------------------------------*/

// create an event manager for the world here?

// each dynamically loaded line will end with this.
//hbl.util.loaded("loader.js");

/*

All config is here !

 */
 
 
hbl.hconfig = function() {

   this.vars    = {

     /* presets */
     poll : false,
     input_width_offset_normal : 48,
     input_width_offset_ie     : 45,
     
     /* theme */
     'theme_url' : '../theme.js' /* set the default theme here */

   };

   this.palette = {};
   this.style   = {};
   this.plugins = new hbl.util.pluginlist();
   this.style_id = {};
   this.style_classes = {};
   this.style_classes_map = {};

   this.merge = function(config_obj, overwrite) {
     // copies data from one config object to another.
     this.copy_hash( this.vars, config_obj.vars, overwrite);
     this.copy_hash( this.style, config_obj.style, overwrite);
     this.copy_hash( this.palette, config_obj.palette, overwrite);

     /* copies the style stuff around */
     this.copy_hash( this.style_id, config_obj.style_id, overwrite);
     this.copy_hash( this.style_classes, config_obj.style_classes, overwrite);
     this.copy_hash( this.style_classes_map, config_obj.style_classes_map, overwrite);

     // ok, I need to figure out the pluginlist issue.
     if(config_obj.plugins && config_obj.plugins.todo) {
       for( i in  config_obj.plugins.todo) {
         this.plugins.add( config_obj.plugins.todo['handler'], config_obj.plugins.todo['priority']);  
       }
     }

   };

   this.copy_hash = function(arr1, arr2, overwrite) {
     if(!arr2) return;

     for(i in arr2) {
       if(overwrite || arr1[i] == undefined ) {
         arr1[i] = arr2[i];
       }
     }    
   };

   this.up = function(config_obj) {
     this.merge(config_obj,true);
   };
   

   
 };


 // These are Add on Functions.

hbl.hconfig.prototype.setMargin = function(val) {
  this.setMargins(val, val, val, val);
};


hbl.hconfig.prototype.setMargins = function(left, top, right, bottom) {  
    this.vars["bottom_margin"]      = bottom;
    this.vars["right_margin"]       = right;
    this.vars["left_margin"]        = left;
    this.vars["top_margin"]         = top;

    this.vars["bottom_margin_px"]      = bottom + "px";
    this.vars["right_margin_px"]       = right  + "px";
    this.vars["left_margin_px"]        = left   + "px";
    this.vars["top_margin_px"]         = top    + "px";  
};


 /** A command to set hab.la to a particular corner. -- Yummy :-) **/
 hbl.hconfig.prototype.setPosition = function(val) {
   this.vars['corner_position'] = val;
   
   if(val == "BR" || val == "BottomRight") {
     this.vars["xcorner"] = "right";
     this.vars["ycorner"] = "bottom";

     // ok change the important CSS
     this.style_classes['habla_window_div_position_floating'] =  'position: $vars["position"]; bottom: $vars["bottom_margin_px"]; right: $vars["right_margin_px"];';
     this.style_classes['habla_window_div_position_floating_ie']    =  'position: $vars["position_ie6"]; bottom: $vars["bottom_margin_px"]; right: $vars["right_margin_px"];'; 


   }else if (val == "BL" || val == "BottomLeft"){
     this.vars["xcorner"] = "left";
     this.vars["ycorner"] = "bottom";    

     // ok change the important CSS
     this.style_classes['habla_window_div_position_floating'] =  'position: $vars["position"]; bottom: $vars["bottom_margin_px"]; left: $vars["left_margin_px"];';
     this.style_classes['habla_window_div_position_floating_ie']    =  'position: $vars["position_ie6"]; bottom: $vars["bottom_margin_px"]; left: $vars["left_margin_px"];'; 

   }else if (val == "TR" || val == "TopRight"){
     this.vars["xcorner"] = "right";
     this.vars["ycorner"] = "top";   
     // ok change the important CSS
     this.style_classes['habla_window_div_position_floating'] =  'position: $vars["position"]; top: $vars["top_margin_px"]; right: $vars["right_margin_px"];';
     this.style_classes['habla_window_div_position_floating_ie']   =  'position: $vars["position_ie6"]; top: $vars["top_margin_px"]; right: $vars["right_margin_px"];'; 

   }else if (val == "TL" || val == "TopLeft"){
     this.vars["xcorner"] = "left";
     this.vars["ycorner"] = "top";  
     // ok change the important CSS
     this.style_classes['habla_window_div_position_floating'] =  'position: $vars["position"]; top: $vars["top_margin_px"]; left: $vars["left_margin_px"];';
     this.style_classes['habla_window_div_position_floating_ie']    =  'position: $vars["position_ie6"]; top: $vars["top_margin_px"]; left: $vars["left_margin_px"];'; 

   }

   // I probably want to get rid of all references to backwards position here.
   // ignore others
   if(hbl.util.BrowserDetect.backwards_position) {
      //Ok we are using an older version of IE.
      //this.ie_6_hack  = " ";
      this.style_classes['habla_window_div_position'] = this.style_classes['habla_window_div_position_floating_ie'];
    }else {
      this.style_classes['habla_window_div_position'] = this.style_classes['habla_window_div_position_floating'];

    }

 };


 /** Add a simple function to set it not inline -- do we want a toggle, that'd be crazy. **/
 hbl.hconfig.prototype.setInline = function(val) {
   // I am absolutely sure I could do this in the window itself.

   if(val) {
       //If we are setting it inline ( and we assume we haven't been rendered yet)
       this.vars["is_inline"] = 1;
       this.style_classes['habla_window_div_position'] = this.style_classes['habla_window_div_position_inline'];
   }else {
     this.vars["is_inline"] = 0;
     if(hbl.util.BrowserDetect.backwards_position) {
       //Ok we are using an older version of IE.
       //this.ie_6_hack  = " ";
       this.style_classes['habla_window_div_position'] = this.style_classes['habla_window_div_position_floating_ie'];
     }else {
       this.style_classes['habla_window_div_position'] = this.style_classes['habla_window_div_position_floating'];

     }
   }
 };

 hbl.hconfig.prototype.setHeight = function(val){
     this.vars["convo_height"] = val;
     this.vars["convo_height_px"] = val + "px";

 };

 hbl.hconfig.prototype.setWidth = function(val) {
   // I am absolutely sure I could do this in the window itself.
   this.vars["width"] = val;
   this.vars["width_px"] = val + "px";
   
   if( hbl.util.BrowserDetect.backwards_dimension ) {
     hbl.util.debug("212/24");
     this.vars["input_width_px"]        = (this.vars["width"] - this.vars["input_width_offset_ie"]) + "px"; 
     this.vars["input_height_px"]       = "24px"; 
   } else {
     hbl.util.debug("202/18");
     this.vars["input_width_px"]        = (this.vars["width"] - this.vars["input_width_offset_normal"]) + "px"; 
     this.vars["input_height_px"]       = "18px"; 
   }


};
 




// We don't have to 'preload' all the stuff below here..
// but we will end up loading it anyway.. so... 




/*
 Loops through all defined
 this.style_classes_map = [classname, classname, classname]

 and

 this.style_id = 'css source code'

 */
hbl.hconfig.prototype.render_all_styles = function(htheme) {
  var div_id;
  for (div_id in this.style_classes_map){
    this.render_element_classes(htheme, div_id);
    
  }
  
  for (div_id in this.style_id){
    this.render_element_id(htheme, div_id);
  }

};

hbl.hconfig.prototype.render_element_classes = function(htheme, div_id) {
  var style_class;
  if(  this.style_classes_map[div_id] ) {
    for (style_class in this.style_classes_map[div_id]){
      /* essentially render each class on this element */

      this.render_class( htheme[div_id], this.style_classes_map[div_id][style_class] );      
    }
  }
};


hbl.hconfig.prototype.render_element_id = function(htheme, div_id) {

  this.render_style( htheme[div_id], this.style_id[div_id]);
};

hbl.hconfig.prototype.render_element = function(htheme, div_id) {
  this.render_element_classes(htheme, div_id);
  this.render_element_id(htheme, div_id);
};

hbl.hconfig.prototype.render_class = function(obj, class_id) {
  //alert(class_id);
  //alert(this.style_classes[class_id]);
  this.render_style( obj, this.style_classes[class_id]);
};

/** Render part of the CSS (mainly replacing $var with value)**/
hbl.hconfig.prototype.render_part = function(part) { 
  var buffer = "";
  if(part == undefined)
    return "";

  // Render as a string
  part = part + "";

  for(var i=0;i<part.length;i++) {
    if(part.substr(i,1) == '$') {
      var done = 0;
      var to_check = "";

      var j;
      for(j=i+1; ((j<part.length) && !done);j++){
        if(part.substr(j,1) != ' '  && part.substr(j,1) != '$' && part.substr(j,1) != "]"){
          to_check += part.substr(j,1);
        }else if(  part.substr(j,1) == ']' ){ 
          done=1;
          to_check += part.substr(j,1);
          i = j;
        }else {
          done = 1;
          i = j-1;
        }

      }

      //in case we hit the end without finishing early
      if(j == part.length){
        i = part.length;
      }

      // ok we have something to check
      try {
        buffer += this.render_part(eval("this." + to_check));
      }catch (e) {
        // I am not sure what to do with errors (deal with this later)
        debug("Error rendering:" + to_check + " " + e.name + " " + e.message);
      }

    }else {
      buffer += part.substr(i,1);
    }

  }
  return buffer;
};

/* I might need to do this to rerender text when setting the person1 and person2 colors */
hbl.hconfig.prototype.render_js_class_style = function(obj, css_class, class_style_name) {
  var list = hbl.util.getElementsByClass(obj, css_class);


  
  for(var i=0;i<list.length;i++) {
    this.render_style(list[i], this.style_classes[class_style_name]  );
    hbl.util.debug("rendered style" + this.style_classes[class_style_name] );
  }
};

hbl.hconfig.prototype.render_js_style = function(obj, css_name) {
    hbl.util.debug("render" + obj);
    return hbl.util.css.set_css(obj, this.render_part( this.style[css_name] ) );
};

hbl.hconfig.prototype.render_style = function(obj, css_value) {
    hbl.util.debug("render style" + obj);
    return hbl.util.css.set_css(obj, this.render_part( css_value ) );
};

hbl.hconfig.prototype.style_to_css = function(css_name, css_val, render_it) {
  var output = '';


  output = css_name + "{" + "\n";
  var parts = css_val.split(";");
  for (var p in parts) {
    if(parts[p].length > 1){
      var va = parts[p].match(/\$[^\]]+\]/);
      if(va ) {
        if(render_it) {
          parts[p] = this.render_part(parts[p]);
        }
        parts[p] = parts[p] + ";\t /* " + va + " */"; 
      }else {
        parts[p] += ";"
      }

      output += "\t" + hbl.util.clean_whitespace(parts[p]) + "\n";
    }
  }
  output += "}\n";

  
  return output;
};




hbl.hconfig.prototype.render_ie_hacks = function() {
  // OK, I am not sure where this code should go.. maybe I'll make another function to handle it?
  //alert("iehack");
  if((hbl.util.BrowserDetect.backwards_position   && ! this.vars["is_inline"]) || this.vars["yahoo_ui"] ) {
      //alert("using iehacks");
      this.monitor = new this.floatMonitor(habla_window);
      this.monitor.start(1);
      //this.monitor.moveTo();
      /*
      hbl.oldonresize = false;
      if(window.onresize && typeof(window.onresize) == "function")  hbl.oldonresize = window.onresize;
      
      window.onresize = function(e) { hbl.util.debug("resized");
                                      if(window.event) e= window.event;
                                      habla_window.eventmgr.handle("document_resized", {"event": e });                                              
                                      if(hbl.oldonresize) hbl.oldonresize(); 
                                    };

  
      hbl.oldonscroll = false;
      
      if(window.onscroll && typeof(window.onscroll) == "function")  hbl.oldonscroll = window.onscroll;
      window.onscroll = function(e) { hbl.util.debug("scrolled");
                                      if(window.event) e= window.event; 
                                      hbl.eventmgr.handle("document_scrolled", {"event": e });
                                      if(hbl.oldonscroll) hbl.oldonscroll(); 
                                    };
      
      // ok now register the events
      hbl.eventmgr.register("document_scrolled", function(arg) { arg["window"].config.ie_position_throttle(arg["event"], arg["window"]); });
      hbl.eventmgr.register("document_resized", function(arg) { arg["window"].config.ie_position_throttle(arg["event"], arg["window"]); });
      hbl.eventmgr.register("habla_window_changed", function(arg) { if(!arg["window"]) return; arg["window"].config.ie_position_throttle(arg["event"], arg["window"]); });
            
      hbl.util.debug("set hacks for IE.. now much cleaner");
      */
  }
    
  //hbl.util.debug("before write");
  //return this.render_part(this.global_template);
};

/*
  I DEFINITELY NEED A BETTER METHOD OF SCROLLING THE WINDOW IN IE6 -- it looks cheap


 */
/* adpted from http://www.codingforums.com/showthread.php?p=637753 */
hbl.hconfig.prototype.floatMonitor = function (hwindow) {
  this.hwindow = hwindow;
  
  this.start = function(arg) {
    /* starts the monitor */
    if(this.monitor_loop) clearTimeout(this.monitor_loop);
    
    this.monitor_loop = setTimeout(function() { hbl.config.monitor.check_status(arg); }, 500);
  };
  
  this.stop = function() {
    if(this.monitor_loop);
      clearTimeout(this.monitor_loop);
  };
  
  this.check_status = function(arg) {
    /* see if we need to move */
    if(!this.moving) this.moveTo(false,arg);
    
    if(this.monitor_loop) clearTimeout(this.monitor_loop);
    this.monitor_loop = setTimeout(function() { hbl.config.monitor.check_status(); }, 300);
  };
  
  this.moveTo = function(force, force_instant) {
    if(!this.moving || force_instant)  { 
      this.moving = true;
      this.stop();
      force = true;
      this.to = this.hwindow.config.get_ie_position(this.hwindow);
    }
    if(!force && !force_instant) return;
    
    var to   = this.to;
    var from = {x:this.hwindow.theme.getX(), y:this.hwindow.theme.getY() };
    
    /* this is pretty much just a loop */
    var diffx = to.x - from.x;
    var diffy = to.y - from.y;
    hbl.util.debug("diffx: " + diffx);
    hbl.util.debug("diffy: " + diffy);
    hbl.util.debug("old: " + from.x + " " + from.y);
    hbl.util.debug("new: " + to.x + " " + to.y);    
    if((Math.abs(diffx) > 5 || Math.abs(diffy) > 5) && !force_instant) {
      this.hwindow.theme.setXY(to.x - diffx * 0.5, to.y - diffy * 0.5);
      hbl.util.debug("moving slowly");

      setTimeout(function(){hbl.config.monitor.moveTo(1);}, 40);
    }else {
      hbl.util.debug("moving quick");
      
        hbl.util.debug(diffx);
        hbl.util.debug(diffy);
        
      this.hwindow.theme.setXY(to.x, to.y);
      //this.to = this.hwindow.config.get_ie_position(this.hwindow);
      this.moving = false;
      this.start();
    }

  }; 
  
};


hbl.hconfig.prototype.get_ie_position = function(hwindow) {
 /* 
   This whole thing needs to be rewritten

  */
  
  // We probably don't care about e.
  var newx = 0;
  var newy = 0;
  // so we need to move the hab.la window
  if( this.vars["xcorner"] == "right") {
    newx = -this.vars["right_margin"] - hwindow.theme.habla_window_div.offsetWidth + (document.documentElement.clientWidth ? document.documentElement.clientWidth: document.body.clientWidth) +  (  document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft );  
  }else {
    newx = this.vars["left_margin"] + (  document.documentElement.scrollRight ? document.documentElement.scrollRight : document.body.scrollRight );
  }
  
  if( this.vars["ycorner"] == "bottom") {
//    newy = -this.vars["bottom_margin"] - hwindow.theme.habla_window_div.offsetHeight + (document.documentElement.clientHeight ? document.documentElement.clientHeight: document.body.clientHeight) +  ( document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop );
      newy = -this.vars["bottom_margin"] - hwindow.theme.habla_window_div.offsetHeight + (document.documentElement.clientHeight ? document.documentElement.clientHeight: document.body.clientHeight) +  ( document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop );


  }else {
    newy = this.vars["top_margin"] +  ( document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop );  

  } 
  
  /* so we know the newx, and the newy 
     we should also know the last time we moved.
     
  */
  //hblDEBUG=1;
  if(!newy) newy = this.vars["top_margin"];
  if(!newx) newx = this.vars["top_margin"];
           
  hbl.util.debug("right:" + this.vars["right_margin"]);
  hbl.util.debug("offset:" + hwindow.offsetWidth);
  hbl.util.debug("width1:" + document.documentElement.clientWidth);
  hbl.util.debug("scroll1:" + document.documentElement.scrollLeft);
  hbl.util.debug("scroll:" + document.body.scrollLeft);
  hbl.util.debug("width:" + document.body.clientWidth);
  
 
  hbl.util.debug("newx:" + newx);
  hbl.util.debug("newy:" + newy);

  return({x:newx, y:newy});
};

/* this function remap's the old palette variables to the new ones -- I'll get rid of this sometime.*/
hbl.hconfig.prototype.remap_palette = function() {  
  var map = {
    mainbg : 'main_bg',
    mainfg : 'main_fg',    
    titlebg : 'title_bg',
    titlefg : 'title_fg',
    buttonbg : 'button_bg',
    buttonfg : 'button_fg',
    buttonhi : 'button_bg_highlight',
    titlebg_highlight : 'title_bg_highlight',
    titlefg_highlight : 'title_fg_highlight'
  };
  
  /* remap here */
  for (var i in map) {
    if(this.palette[i] ){
      this.palette[map[i]] = this.palette[i];
    }
  }
  
  /* extra */
  if(this.palette['buttonfg'] ) {
    this.palette['button_fg_highlight'] =  this.palette['buttonfg'];
  }

};

/* a few simple things */
hbl.hconfig.prototype.init_from_config = function() {
  /* set the width */
  this.setWidth(this.vars["width"]);
  
  /* set the height */
  this.setHeight(this.vars["height"]);

  if(! this.vars["is_inline"] ) {
    this.setPosition(this.vars["corner_position"]);
  
  }else {
    this.setInline(this.vars["is_inline"]);
  }
  
  // Opera fix:
  if( hbl.util.BrowserDetect.browser=="Opera" ) {
    this.vars["poll"] = true;
  }
  
};

/* 
  ahh.. smart -re-render 

  it would be a really good idea.. to roll some of these function out of the main core files.. 
  
  so you have like util_extra.js -- which has all the utility functions that aren't really needed
  
  and config_extra.js -- which has the same.. and you can include them on the 'theming pages'.. 
  
  
*/






/* ---------------------------------------------------------------------------------------------------------------------*/
/* This is a DEFAULT CONFIG OBJECT */

hbl.hconfig.prototype.load_defaults = function() { 
  
  // Plugins here?
  // Probably should eventually make this a priority queue
  this.plugins = new hbl.util.pluginlist();
  
  // Ok load the config for config
  this.palette = new Array();
  this.palette['main_bg']   = '#ffffff';
  this.palette['main_fg']   = '#000000';
  
  this.palette['title_bg']  = '#333333';
  this.palette['title_fg']        = '#ffffff';
  this.palette['title_fg_hover']  = '#ffffff';  
  
  
  this.palette['button_bg'] = '#333333';
  this.palette['button_fg'] = '#ffffff';
  
  this.palette['button_fg_highlight'] = '#ffffff';
  this.palette['button_bg_highlight'] = '#333333';
  
  this.palette['button_fg_hover'] = '#ffffff';
  this.palette['button_bg_hover'] = '#333333';
  
  this.palette['control']  = '#cccccc';
  
  this.palette['link']     = '#e75917';
  this.palette['link_highlight']   = '#ff0000';

  this.palette['local']    = '#ff0000';   /* people  colors -- need better name*/
  this.palette['remote']   = '#333333';
  
  this.palette['title_bg_highlight']  = '#333333';
  this.palette['title_fg_highlight']  = '#00ffff';
                
  this.vars = new Array();
  
  this.vars["bottom_margin"]      = 10;
  this.vars["right_margin"]       = 10;
  this.vars["left_margin"]        = 10;
  this.vars["top_margin"]         = 10;
  
  this.vars["bottom_margin_px"]      = "10px";
  this.vars["right_margin_px"]       = "10px";
  this.vars["left_margin_px"]        = "10px";
  this.vars["top_margin_px"]         = "10px";
  
  
  this.vars["myname"] ="you";
  
  this.vars["corner_position"]   = "BR";
  
  this.vars["position"]           = "fixed";
  this.vars["position_ie6"]       = "absolute";
  
  this.vars["width"]              = 250;  
  this.vars["width_px"]           = "250px";
  

  this.vars["input_width_offset_normal"] = 48;
  this.vars["input_width_offset_ie"] = 45;
  
  this.vars["say_text"] = "Say: ";
  this.vars["input_box_size"]   = "40";
  this.vars["check_for_status"] = "Hab.la Chat (startup)";

  /* more input box stuff */
  this.vars["resize_length"] = 25; // how many characters before we make the input box bigger
  this.vars["resize_input_height"] = 60;   // when we hit the max, what do we resize to
  this.vars["disable_expand_text_input"] = false;

  this.vars["disable_set_cookies"] = false;
  
  
  this.vars['local_user_display_name'] = "&gt;";
  
  /* enable google analytics by default */
  this.vars["disableGoogleAnalytics"] = 0;
  this.vars["expandOnMessageReceived"] = 0;
  
  // Google translate
  this.vars["language"] = "en";
  this.vars["enableLanguageTranslation"] = false;
  this.vars["expandOnFirstMessageReceived"] = 1;
  
  
  // Plugin PATH
  this.vars["plugin_path"] = "http://static.hab.la/js/plugins/";

  //div id:
  this.vars["divid"]  = "habla_window_div";

  /* let people select polling or sleeping */
  this.vars["poll"] = false;
  if( hbl.util.BrowserDetect.browser=="Opera" ) {
    this.vars["poll"] = true;
  }

  /* add to docs */ 
  this.vars["xcorner"] = "right";
  this.vars["ycorner"] = "bottom";
  
  //----------- kevin's stuff
  // hbl.hwindow.dimension_hack is what was added here
  
  /* you had this backwards. */
  hbl.util.debug("bd is " + hbl.util.BrowserDetect.backwards_dimension);
  
  /* set the width */
  this.setWidth(this.vars["width"]);
  
  //-----------------------------------
  this.vars["convo_height_px"]       = "155px";
  this.vars["convo_height"]          = 155;       /* maybe convert this to min height and max height so it can grow */
  this.vars["height"]                = 155;
  this.vars["panel_offset"]          = 20;


  
  this.vars["in_chat_text"]       = "Now Chatting";
  this.vars["before_chat_text"]   = "Type To Chat";
  this.vars["not_available_text"] = "Not Available";
  this.vars["busy_text"]          = "Busy";
  this.vars["away_text"]          = "Away";
  
  this.vars["offline_message"]      = "<em>No one is available for chat right now.&nbsp; Please try again later.</em>";
  //this.vars["welcome_msg"]        = "Welcome to my website.  You can use this chat window to chat with me.";
  this.vars['welcome_msg']          = '';
  this.vars["busy_message"]         = "<em>No one is available for chat right now.&nbsp; Please try again later.</em>";
  // show the hide/close/buttons
  this.vars["enable_buttons"]       = 1;
  this.vars["hide_min_max_buttons"] = 0;
  
  this.vars["local_name_override"]  = undefined;

  this.vars["url_handler_target_window"]  = "_top";
  this.vars["url_handler"]                = "http://static.hab.la/js/html/url_handler.html";
  this.vars["url_local_pattern"]          =  document.domain;
  
  this.vars["parse_links"]         = 1;
  this.vars["is_inline"]           = 0;
  this.vars["start_expanded"]      = 0;
  this.vars["start_hidden"]        = 0;
  this.vars["hide_not_available"]  = 0;  
  this.vars["append_to_body"]      = 1;
  this.vars["show_away"]           = 0;  // don't show away messages by default
  this.vars["hide_when_away"]      = 0;
  this.vars["show_away_as_header"] = 0;
  

  
  this.vars['habla_special_div_show_type'] = "block";
  
  // for flash Icons plugin:
  this.vars["flash_icons"]  = 1;
  this.vars["default_flash_off_icon"] = "http://static.hab.la/js/images/white.ico";
  this.vars["default_flash_on_icon"]  = "http://static.hab.la/js/images/orange.ico";

  /* Style is getting phased out in favor of a much cooler way of doing this */

  /* I probably want to eventually move all this directly into the theme */
  /* that would make a lot of sense */

  this.style_classes_map = {};
  this.style_id = {};
  this.style_classes = {};

  this.style = new Array();
  this.style["habla_link_a"]        = 'font-family: verdana, sans-serif; text-transform: uppercase; font-size: 9px; letter-spacing: 2px; font-weight: bold; color: $palette["link"]';
  
  // I need to move this somewhere else


};



/*-------------------------------------------------- this can be moved to a config_extra.js --------------------------------- */





/* This file has all the utility functions */

/* this might not really belong here */

/*
if(!window.hbl ) {
  hbl = {};
  hbl.util = {}; 
  hbl.util.loaded = function() {};
}
*/

/* We need Plugin List for the config object */
hbl.util.pluginlist = function(){
  this.todo = new Array();
  this.defined = {};
  
  // Register a handler
  this.add = function(handler, priority){
      if(priority == undefined) {
        priority  = 999;
      }
      if(handler == undefined) return; 
      
      if(this.defined[handler.name] ) return; // won't let you insert the same plugin two times.
      this.defined[handler.name] = 1;
      
      this.todo.push({"handler" : handler, "priority" : priority});
      this.todo = this.todo.sort( function(a,b) { return (a["priority"] - b["priority"]); });
  };
  
  // Handler
  this.load = function(arg) {
    var ret = arg;
    for (var i=0;i<this.todo.length;i++) {
      hbl.util.debug("plugin list running");
      
      // added code to make it only load each plugin one time.
      if( this.todo[i] && this.todo[i]["handler"] && typeof(this.todo[i]["handler"]["load"]) == "function" && this.defined[this.todo[i]["handler"].name] ==1) {
        hbl.util.debug("pluginlist loaded" + this.todo[i]["handler"].load.toString() );
        this.todo[i]["handler"].load(arg);
        this.defined[this.todo[i]["handler"].name]=2; 
      }
    }
    return ret;
  }; // end handle   
};





/* I need the debugging function: */
hbl.util.debug = function(x) {
  if (hblDEBUG == undefined || !hblDEBUG) return;

  //x = timestamp() + " " + x;
  if( document.getElementById("hbl_debug") ) {
    d = document.getElementById("hbl_debug");
    d.innerHTML = x + '<br />' + d.innerHTML;
  } else if( window.console ) {
    window.console.log(x);
  } 
  // opera does not like you trying to check and see if a specific variable exits.
  else if( !hblnoconsole  && console != undefined && console.log ) {
    console.log(x);
  } else {
    alert(x);
  }
};


hbl.util.get_body = function() {
  var tmp = document.getElementsByTagName("html");
  var html = null;
  if( tmp.length<1 ) {
    html = document.createElement("html");
    document.appendChild(html);
  } else {
    html = tmp[0];
  }
  tmp = document.getElementsByTagName('body');
  var html_doc = null;
  if( tmp.length > 0 ) {
    html_doc = document.getElementsByTagName('body').item(0);
  } else {
    html_doc = document.createElement('body');
    html.appendChild(html_doc);
  }

  return html_doc;
};

hbl.util.simple_load_js_async = function(url){
  /*
    Some hacks for deleting the div after it's used
  */  
  
  html_doc = hbl.util.get_body();
  
  var js = document.createElement('script');
  js.setAttribute('language', 'javascript');
  js.setAttribute('type', 'text/javascript');
  js.setAttribute('src',  url);
  hbl.util.debug( "docall: hitting " + url );
  /*if( getmsg || html_doc.childNodes.leng*/
  html_doc.appendChild(js);
};




hbl.util.last_js_id = 0;

   
   /* pipeline
   // a pipeline is essentially a Queue that processes some content, and
   // moves through the pipeline, as long as the return value from the previous step is not undefined. (otherwise it quits)
      it is very similar to a event manager.
   */
   
hbl.util.pipeline = function(hosto){
 this.todo = new Array();
 this.host_obj = hosto;
 
 // Register a handler
 this.add = function(handler, priority){
     if(priority == undefined) {
       priority  = 999;
     }
     this.todo.push({"handler" : handler, "priority" : priority});
     this.todo = this.todo.sort( function(a,b) { return (a["priority"] - b["priority"]); });
 };
 
 /* Handler */
 this.run = function(arg) {
   var ret = arg;
   for (var i=0;i<this.todo.length;i++) {
     if( this.todo[i] && typeof(this.todo[i]["handler"]) == "function") {
       hbl.util.debug("pipeline" + this.todo[i]["handler"].toString() );
       ret = this.todo[i]["handler"](arg,this.host_obj);
       
       if(ret == undefined) return undefined;
       arg = ret;
     }
   }
   return ret;
 }; /* end handle */
 
 this.setTarget = function(tt) {
   this.host_obj = tt;
 };

};


/* A helper function to load plugins */
hbl.util.pluginloader = function(conf, client, hwindow){
 HABLA_PLUGIN_ARGS = {"conf" : conf, "client" : client, "hwindow" : hwindow};
 conf.plugins.load({"conf" : conf, "client" : client, "hwindow" : hwindow});
};
   
hbl.util.timestamp = function() {
 var now = new Date();
 return now.toUTCString();
};



hbl.util.reportException = function(e) {};

 
hbl.util.set_cookie = function(name, value, expire, extra) {
 var d = new Date();
 if(expire == undefined)
  expire = 24*60*60*1000;
  
 d.setTime( d.getTime() + expire );
 var expires = d.toGMTString();

 var value = escape(value);
 var cook  = name + "=" + value + "; expires=" + expires + "; path=/"; 
 if(extra != undefined ){
   cook += extra;
 }
 document.cookie = cook;

 /*
 document.cookie = name + "=" + value + ";"
 + (expires != -1 ? " expires=" + expires + ";" : "")
 + (path ? "path=" + path : "")
 + (domain ? "; domain=" + domain : "")
 + (secure ? "; secure" : "");
 */  
};

hbl.util.get_cookie = function(name) {
 var nameEQ = name + "=";
 var ca = document.cookie.split(';');
 for(var i=0;i < ca.length;i++) {
   var c = ca[i];
   while (c.charAt(0)==' ') c = c.substring(1,c.length);
   if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
 }
 return null;
};

hbl.util.load_js_async = function(url){
 /*
   Some hacks for deleting the div after it's used
 */
 hbl.util.last_js_id += 1;
 var scriptid = "jscript" + hbl.util.last_js_id;
 
 if( url.indexOf("?") > -1){
   url += "&" + scriptid;
 }else {
   url += "?" + scriptid;
 }
 
 html_doc = hbl.util.get_body();
 
 var js = document.createElement('script');
 js.setAttribute('language', 'javascript');
 js.setAttribute('id', scriptid);    
 js.setAttribute('type', 'text/javascript');
 js.setAttribute('src',  url);
 hbl.util.debug( "docall: hitting " + url );
 /*if( getmsg || html_doc.childNodes.leng*/
 html_doc.appendChild(js);
};

hbl.util.remove_element = function(element_id) {
 var el = document.getElementById(element_id);
 if(el != undefined && el.parentNode != undefined)
   el.parentNode.removeChild(el); 
};


/* If the object is not and object, turns it into a DOM object
   and then returns it.
*/
hbl.util.as_dom = function (element ){
  var ret;
  if(typeof(element) == "object") {
    return element;
  }
  ret = document.createElement("span");
  ret.innerHTML = element;
  return ret;
};
  


hbl.util.find_or_create_el = function (elid, el_type, append_to ){
 var ret = document.getElementById(elid);
 if(!ret  || ret.length < 0) {
   
   if(el_type == undefined)
    el_type = "div";
   
   ret = document.createElement(el_type);
   ret.setAttribute("id",elid);

   if(append_to != undefined) {
     append_to.appendChild(ret);
   }
 }
 return ret;
};
   
/** Get's a DIV or created a new one **/
hbl.util.find_or_create_div = function (divid, append_to ){
  return hbl.util.find_or_create_el(divid, "div", append_to);
};

   
hbl.util.hide_div = function(div) {
  if(typeof(div) != "object") {
    div = document.getElementById(div);
  }
  if(div != undefined) {
    div.style.display = "block";
	document.getElementById("habla_window_div").style.display = "none";
    return true;
  }
    return false;
};
   
hbl.util.show_div = function(div, show_val) {
 if( show_val == undefined) show_val = "block";
 
 if(typeof(div) != "object") {
   div = document.getElementById(div);
 } 
 if(div != undefined) {
   div.style.display = show_val;
   return true;
 }
 return false;
};    


hbl.util.clean_whitespace = function(val) {
     /* I need a regex here as well */
     val = val.replace(/^\s*/,"");
     val = val.replace(/\s*$/,"");
     val = val.replace("'","");
     return val;
};

/** this is a cool trick I learned at nokia -- it's a good way to stop multiple event handlers from firing at the same time **/
   
hbl.util.throttle_length = 700;
hbl.util.throttle_ts = null;

hbl.util.doThrottle = function() {
  /* timestamp to throttle */
   var ts = (new Date()).getTime();
   var doThr = false;
   if (hbl.util.throttle_ts && (ts - hbl.util.throttle_ts) < hbl.util.throttle_length)
       doThr = true;
   hbl.util.throttle_ts = ts;
   return doThr;
};
   
   /* And now, some cool stuff to do browser detection
      http://www.quirksmode.org/js/detect.html
      ok it's kind of ugly
      but I don't feel like rewriting it
   */
hbl.util.BrowserDetect = {
     _initialized : false,
   init: function() {
     if( this._initialized ) return;
     /*this.supported = false; */
     this.browser = this.searchString(this.dataBrowser,1) || "An unknown browser";
     this.version = this.searchVersion(navigator.userAgent)
       || this.searchVersion(navigator.appVersion)
       || "an unknown version";
     this.OS = this.searchString(this.dataOS) || "an unknown OS";
     
     this.backwards_position  = false;
     this.backwards_dimension = false;
     
     
     if( document.compatMode=="BackCompat"){
       this.backwards_dimension  = true;
     }
     hbl.util.debug(document.compatMode + " " + this.backwards_dimension);
     
     /* 
       Do the backcompat stuff here I guess.
       kevin's stuff 
     */
     if(this.browser == "Explorer") {
       this.backwards_position  = true;
       if( (this.version >= 7 || (document.body && typeof document.body.style.maxHeight != "undefined")) && document.compatMode!="BackCompat" ) {
           /* it's IE7 in CSS1Compat */
           hbl.util.debug("IE7 compat mode");
           this.backwards_position  = false;
        }
     }
     /* end back compat stuff */

     this._initialized = true;
     
   },
   searchString: function (data, browser) {
     for (var i=0;i<data.length;i++) {
       var dataString = data[i].string;
       var dataProp = data[i].prop;
       this.versionSearchString = data[i].versionSearch || data[i].identity;
       if (dataString) {
         if (dataString.indexOf(data[i].subString) != -1) {
           if(browser != undefined ) this.supported = data[i].supported;
           return data[i].identity;
         }
       }
       else if (dataProp){
         if(data[i].identity) this.supported = data[i].supported;
         return data[i].identity;
       }
     }
   },
   searchVersion: function (dataString) {
     var index = dataString.indexOf(this.versionSearchString);
     if (index == -1) return;
     return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
   },
   dataBrowser: [
     {   string: navigator.userAgent,
       subString: "OmniWeb",
       versionSearch: "OmniWeb/",
       identity: "OmniWeb"
     },
     {
       string: navigator.vendor,
       subString: "Apple",
       identity: "Safari",
       supported: "1"
     },
     {
       prop: window.opera,
       identity: "Opera",
       supported: "1"
     },
     {
       string: navigator.vendor,
       subString: "iCab",
       identity: "iCab"
     },
     {
       string: navigator.vendor,
       subString: "KDE",
       identity: "Konqueror"
     },
     {
       string: navigator.userAgent,
       subString: "Firefox",
       identity: "Firefox",
       supported: "1"
     },
     {
       string: navigator.vendor,
       subString: "Camino",
       identity: "Camino",
       supported: "1"
     },
     {   /* for newer Netscapes (6+) */
       string: navigator.userAgent,
       subString: "Netscape",
       identity: "Netscape",
       supported: "1"
     },
     {
       string: navigator.userAgent,
       subString: "MSIE",
       identity: "Explorer",
       versionSearch: "MSIE",
       supported: "1"
     },
     {
       string: navigator.userAgent,
       subString: "Gecko",
       identity: "Mozilla",
       versionSearch: "rv",
       supported: "1"
     },
     {     /* for older Netscapes (4-) */
       string: navigator.userAgent,
       subString: "Mozilla",
       identity: "Netscape",
       versionSearch: "Mozilla",
       supported: "1"
     }
   ],
   dataOS : [
     {
       string: navigator.platform,
       subString: "Win",
       identity: "Windows"
     },
     {
       string: navigator.platform,
       subString: "Mac",
       identity: "Mac"
     },
     {
       string: navigator.platform,
       subString: "Linux",
       identity: "Linux"
     }
   ]

};/* end browserdetect */
   
/* More util stuff? [incase I want to be funky] */   
hbl.util.decode_base64 = function(input,eval_it) {
       
     var output = "";
     if(window.atob ) {
       output = window.atob(input);
     }else {
       var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 
       var chr1, chr2, chr3;
       var enc1, enc2, enc3, enc4;
       var i = 0;

       /* remove all characters that are not A-Z, a-z, 0-9, +, /, or = */
       input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

       do {
          enc1 = keyStr.indexOf(input.charAt(i++));
          enc2 = keyStr.indexOf(input.charAt(i++));
          enc3 = keyStr.indexOf(input.charAt(i++));
          enc4 = keyStr.indexOf(input.charAt(i++));

          chr1 = (enc1 << 2) | (enc2 >> 4);
          chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
          chr3 = ((enc3 & 3) << 6) | enc4;

          output = output + String.fromCharCode(chr1);

          if (enc3 != 64) {
             output = output + String.fromCharCode(chr2);
          }
          if (enc4 != 64) {
             output = output + String.fromCharCode(chr3);
          }
       } while (i < input.length);
    }
  
  if(eval_it) {
    var output1;
    eval("output1 = " + output);
    return output1;
  }
  
  return output;
};




hbl.util.css = {
   /** rendering CSS with javascript utility functions **/
   set_css : function(objj, css) {
     hbl.util.debug("setting:  " + objj + " " + css);
     
     if(!objj) {
       hbl.util.debug("No object to render");
        /* if there is no obj.. return */
       return false;
     }
     
     /* takes an object and a css fragment, attempts to parse it and render it as best as possible.*/
     var parts = css.split(";");
     for(var i=0;i<parts.length;i++) {
       namval = parts[i].split(":");
       if(namval.length > 2) {
         /* javascript split doesn't work the way I want it to */
         var t = namval.shift();
         var v = namval.join(":");
         namval = [t,v];
       }
       try {
         if(namval.length == 2) {
           name = hbl.util.css.map_css_to_js(namval[0]);
           if(name.indexOf("|") > 0) {
               names = name.split("|");

               for(var j=0;j<names.length;j++) {

                 eval("objj.style." + names[j] + "= " + "'" + hbl.util.clean_whitespace(namval[1]) + "'");
               }
           }else {
             hbl.util.debug( "objj.style." + name + "= " + "'" + hbl.util.clean_whitespace(namval[1]) + "'" );
             eval("objj.style." + name + "= " + "'" + hbl.util.clean_whitespace(namval[1]) + "'");
           }
         }
       }catch (e) {
         hbl.util.debug("objj.style." + hbl.util.css.map_css_to_js(namval[0]) + "= " + "'" + hbl.util.clean_whitespace(namval[1]) + "'");
         hbl.util.debug("error css Rendering:" + e.message);
       }
     }
   },
   /** a map of relations between css and js **/
   css_to_js : {
     "float" : "cssFloat|styleFloat"
   },
   
   map_css_to_js : function(name) {
     /* I need to learn how to do regex */
     name = hbl.util.clean_whitespace(name);
     /*
      hbl.util.debug(hbl.css.css_to_js);
      hbl.util.debug("map to CSS");
     */
     /* something to map the name to the correct element*/
     
     if( hbl.util.css.css_to_js[name] && hbl.util.css.css_to_js[name].length > 0)
       return hbl.util.css.css_to_js[name];

     /* A heuristic conversion might be to split by - and then make the next letter capitalized*/
     var parts = name.split("-");
     name = "";
     for(var i=0;i<parts.length;i++){
       tn= parts[i].toLowerCase();
       if(i > 0)
         tn = tn.substr(0,1).toUpperCase() + tn.slice(1);
       name += tn;
     }

     return name;
   }
 
   
 }; /* end css */



 hbl.util.getElementsByClass = function(node, classname) {
   if(!node) node = document.getElementsByTagName("body")[0];
   var a = [];
   var re = new RegExp('\\b' + classname + '\\b');
   var els = node.getElementsByTagName("*");
   for(var i=0,j=els.length; i<j; i++) {
     if(re.test(els[i].className))a.push(els[i]);
     hbl.util.debug("Got " + els[i].className );
   }
   return a;

 };






// end on this.
//hbl.util.loaded("util.js");
/*
  One idea is to make the client a bit smarter than it is now.
  
  That is to say..  to have it handle more advanced forms of conversation,
  i.e. have a more 'packet' oriented approach to support multiple operators.. multiple
  clients.. etc.. using the same smart communication method.
  
  The big idea here is to move all the client stuff into one place.
  so it will make the most sense.
  
  I need to remember to add the new HBLID stuff as well
  and the new.. method of keeping track of the load balancing session as well.
  
  It should be designed so that it can run without a front-end.
  
 */

 /** hbl.client has everything to do with the JSON client for hab.la **/



hbl.client  = {
   // Add the pipeline here for fun.
   incoming_pipeline : new hbl.util.pipeline(),
     
   /** manager to handle polling **/
   pollingmanager:  {
     /* constants */
     LAG_CONSTANT: 25 *1000,                                // How much lag do we expect
     GET_MESSAGES_TIMEOUT: 55 *1000,                       // how long before we consider a get_messages failed
     ACTIVE_CHAT: 70 *1000,
     RECENTLY_ACTIVE_CHAT: 120 *1000,
     
     FIRST_POLL_TIME: 0.8 *1000,
     RECENTLY_ACTIVE_CHAT_TIMEOUT: 25 * 1000,
     IGNORE_LAG_TIME: 10 * 1000,  // ignore failure that are under 10 secs long
     
     MAX_POLL_TIME: 120 * 60 * 1000, // 120 minutes?
     /* checking for failure vars */
     check_failed_polling_rate: 20 *1000, 
     
     THROTTLE_THRESHOLD_MESSAGE: 5*60*1000, // the max time we'll let something be in fast polling mode, without a message reply
     LAST_ACTION_THROTTLE_THRESHOLD: 1 * 1000,
     LAST_ACTION_THROTTLE_MIN: 2*1000,
     
     /* internal polling variables */
     last_poll_start_time:    0,    // time of last poll start
     last_poll_end_time:      0,    // time of last poll end
     first_poll_time:         0, // time of first poll ever.
     last_action_time:        0,    // time of last 'action'
     last_sent_message_time:  0, // last sent message .. as a failsafe
     
     next_poll_time:          0,
     current_poll_rate: 0,   // rate of polling
     
     check_for_failed_timer: false,
     
     last_poll_timer: false,   // polling timer name
     
     event_driven: false,
     expected_finish: 0,
     
     rate_array : false,
     
     build_rates: function() {
       if(hbl.client.pollingmanager.rate_array != false)
         return;
       
       hbl.client.pollingmanager.rate_array = [
       /* ones called at highest rate come first */
       /* 
          really.. I don't think it's really that important to CHECK at all.. when no op is online 
          at least in the FREE version, this seems like a waste of cycles 
       */
       /* OFF LINE */
       
       { // The op is offline and just started the session
         last_action:  0, 
         time_on_page: 4*60*1000, // for the first 4 minutes check every 45 seconds for an op coming online
         timeout: 45 * 1000,
         opavailable: false
       },
       { // The op is offline and < 15 minutes on page
         last_action:  0, 
         time_on_page: 2*60*60*1000, // for the next 2 hours check every 25 minutes for an op coming online
         timeout: 25 * 60 * 1000,
         opavailable: false
       },        
       /* otherwise , check max time, every 2 HOURS */
        
        /** CHATTING **/
       { // The get_messages and NOT POLLING CASE                  --- you know if could be this case that's breaking somewhere else?
         polling: false,
         last_action:  70 * 1000, // they chatted in the last 70 seconds
         time_on_page: 0,
         timeout: 0,
         opavailable: true
       },
           
       { // The POLLING CASES
         polling: true,
         last_action:  5 * 1000, // they chatted in the last 5 seconds
         time_on_page: 0,
         timeout: 600,
         opavailable: true
       },
       { // The POLLING CASES
         polling: true,
         last_action:  15 * 1000, // they chatted in the last 15 seconds
         time_on_page: 0,
         timeout: 800,
         opavailable: true
       },                
       { // The POLLING CASES
         polling: true,
         last_action:  30 * 1000, // they chatted in the last 30 seconds
         time_on_page: 0,
         timeout: 1500,
         opavailable: true
       },        
       { // The POLLING CASES
         polling: true,
         last_action:  60 * 1000, // they chatted in the last 60 seconds
         time_on_page: 0,
         timeout: 3 * 1000,
         opavailable: true
       },    
       { // The POLLING CASES
         polling: true,
         last_action:  90 * 1000, // they chatted in the last 90 seconds
         time_on_page: 0,
         timeout: 10 * 1000,
         opavailable: true
       },    
       
       /** ONLINE **/
       { // The op is online and just started the session
         last_action:  0, // they chatted in the last 70 seconds
         time_on_page: 2*60*1000, // for the 2 minutes
         timeout: 20 * 1000,
         opavailable: true
       },
       { // The op is online and just started the session
         last_action:  0, // they chatted in the last 70 seconds
         time_on_page: 5*60*1000, // for the 2 minutes
         timeout: 25 * 1000,
         opavailable: true
       },
       { // The op is online and just started the session
         last_action:  0, // they chatted in the last 70 seconds
         time_on_page: 15*60*1000, // for the 15 minutes
         timeout: 30 * 1000,
         opavailable: true
       },
       { // The op is online and just started the session
         last_action:  0, 
         time_on_page: 45*60*1000, // for the 15-45 minutes 
         timeout: 45 * 1000,       // every 45 seconds
         opavailable: true
       },
       { 
         last_action:  0, 
         time_on_page: 2*60*60*1000, // for the first 45minutes- 2 hours
         timeout:  2 * 60 * 1000,    // 2 minutes 
         opavailable: true
       },
        { 
         last_action:  0, 
         time_on_page: 8*60*60*1000, // for the first 2-8 hours
         timeout:  15 * 60 * 1000,    // 15 minutes 
         opavailable: true
       },
       { 
         last_action:  0, 
         time_on_page: 24*60*60*1000, // for the first 8-24 hours
         timeout:  30 * 60 * 1000,    // 30 minutes 
         opavailable: true
       }
       ];
     },
     
     get_rate : function( opavailable, opbusy, passive_mode, force_poll) {
       /*
        * ok, we know when the 'next' poll should fire (next)
        * we know when the 'last' poll we set was requested (start)
        * we know when the 'last' poll we set returned results (send)
        * we know when the last 'action' we received was
        */
       
       // Now we need to use this information to 'keep it real'
       // PASSIVE timeout is irrespective of how often we poll, so we can poll as
       // frequently or infrequently as we want.
       // what we have to do here is determine that frequency.
       
       // the only external information I need here is.
       // 'are they using OPERA, or not' and is it set to FORCE POLL.
       
    
       
       // 
       var now = new Date();
       var rate = -1;
       
       if( hbl.client.pollingmanager.first_poll_time == 0 && hbl.client.pollingmanager.rate_array == false){
         // it's the first time we've been called.
         // just go for it man
         hbl.client.pollingmanager.build_rates();
         return hbl.client.pollingmanager.FIRST_POLL_TIME;
       }
       
       // return the forced passive -- max poll time.
       if( passive_mode )
         return hbl.client.pollingmanager.MAX_POLL_TIME;
       
       var time_on_page = now.getTime() - hbl.client.pollingmanager.first_poll_time;
       var last_action  = now.getTime() - hbl.client.pollingmanager.last_action_time;
       
       // This is a failsafe, IF the last action was really recent, BUT the last sent message was
       // a while ago.. defer to the last sent message
       
       if( last_action < hbl.client.pollingmanager.LAST_ACTION_THROTTLE_THRESHOLD &&  now.getTime() -  hbl.client.pollingmanager.last_sent_message_time > hbl.client.pollingmanager.THROTTLE_THRESHOLD_MESSAGE ){
         // this is just incase we get stuck in some crazy raction loop, 
         // we have this failsafe here.
         last_action = hbl.client.pollingmanager.LAST_ACTION_THROTTLE_MIN;
       }
       
       // opavailable
       // opbusy
       hbl.util.debug(" time on page: " + time_on_page);
       hbl.util.debug(" last_action: " + last_action);
       
       
       // If I wanted to be cool, I could make this a SEARCH call rather then N time.
       var ar = hbl.client.pollingmanager.rate_array;
       for (i in ar) {
         hbl.util.debug("RUNNING HERE");
         hbl.util.debug( ar[i]);
         
         if( (ar[i]['polling'] == force_poll || ar[i]['polling'] == undefined ) &&  ar[i]['opavailable'] == opavailable 
             && (ar[i]['passive_mode'] == undefined || ar[i]['passive_mode'] == passive_mode) && (ar[i]['time_on_page'] > time_on_page  || ar[i]['last_action'] > last_action) /*&& (ar[i]['opbusy'] == opbusy || ar[i]['opbusy'] == undefined)*/ ) {
               return ar[i]['timeout'];
             }
       } 
       
       // if no match, return the max poll time.
       return hbl.client.pollingmanager.MAX_POLL_TIME;
       
     },
     
       // I need to know if they are available or not
                                                                   // this last arg is passed to get_messages
     start_next_poll : function( opavailable, opbusy, force_poll, force_passive, poll_mode ) {
       
       // Start it if we need to:
       if(hbl.client.pollingmanager.check_for_failed_timer == false){
         hbl.client.pollingmanager.check_for_failed_timer = setTimeout("hbl.client.pollingmanager.check_failed_polling()", hbl.client.pollingmanager.check_failed_polling_rate );
       }
       // Starts a new poll, killing off the last one, if necessary
       // I don't always want to do this.
       

       var rate = hbl.client.pollingmanager.get_rate( opavailable, opbusy, force_passive, force_poll );
       hbl.util.debug("Opavailable : " + opavailable + " busy:" + opbusy + " -- " + force_poll);
       
       hbl.util.debug("CURRENT RATE IS: " + rate);
       hbl.util.debug("PASSIVE: " + force_passive);
       
       // Store the RATE
       hbl.client.pollingmanager.current_poll_rate = rate;
       
       
       // Set the time that we expect the next poll.
       var now = new Date();
       if(hbl.client.pollingmanager.next_poll_time > now.getTime() && hbl.client.pollingmanager.next_poll_time < now.getTime() + rate){
         // Do nothing, we are good the way we are now.
         
       }else {
         /* if we have an existing timer that's going to fire BEFORE the new one we are about to add */
         /* don't add it, let the current one fire                                                   */
         
         if(hbl.client.pollingmanager.last_poll_timer != false) {
           // kill it off
           window.clearTimeout(hbl.client.pollingmanager.last_poll_timer);
         }
         // when is the next one suppose to fire ( this will help the CHECKER )
         hbl.client.pollingmanager.next_poll_time = now.getTime() + rate;
       
         if(rate == 0 ) {
           hbl.client.pollingmanager.expected_finish = hbl.client.pollingmanager.GET_MESSAGES_TIMEOUT + now.getTime();
         }else {
           hbl.client.pollingmanager.expected_finish = hbl.client.pollingmanager.next_poll_time + hbl.client.pollingmanager.LAG_CONSTANT;            
         }
       
         // go for it :-)
         hbl.client.pollingmanager.last_poll_timer = window.setTimeout( "hbl.client.pollingmanager.pollfunc(" + (poll_mode == true? "true" : "") + ")" , rate);   
       }
       
       
     },
     
     pollfunc : function(arg) {
       // keeps track of the last time the poll started.
       var now = new Date();
       hbl.client.pollingmanager.last_poll_start_time = now.getTime();
       
       if(hbl.client.pollingmanager.first_poll_time == 0 ) {
         hbl.client.pollingmanager.first_poll_time = now.getTime();
       }
        
       // we'll jsut hardcode this for now.
       hbl.client.get_messages(arg);
     },
     
     // This function is called when the last poll called finishes. (i.e. we get a callback)
     finished_last_poll : function () {
       var now = new Date();
       hbl.client.pollingmanager.last_poll_end_time = now.getTime();        
     },
     
     saction : function() {
       var now = new Date();
       hbl.client.pollingmanager.last_sent_message_time = now.getTime();        
     },      
     // this is called whenever we get a 'new action'
     raction : function() {
       var now = new Date();
       hbl.client.pollingmanager.last_action_time = now.getTime();        
     },
     set_expected_finish : function(val) {
       var now = new Date();
       hbl.client.pollingmanager.expected_finish = val + now.getTime();        
     },      
     /* 
       This function checks to see if the polling is fucked up
       if it is, it starts polling again 
     */
     check_failed_polling : function () {
       var now = new Date();
       if( hbl.client.pollingmanager.last_poll_start_time > hbl.client.pollingmanager.last_poll_end_time &&
          now.getTime() > hbl.client.pollingmanager.expected_finish && 
          hbl.client.pollingmanager.current_poll_rate > hbl.client.pollingmanager.IGNORE_LAG_TIME){
         /* to summarize if LAST TIME POLL STARTED > LAST TIME FINISHED
            AND IT's NOW > the EXPECTED FINISH TIME */
         
         // We have failed.
         // clear last timer
         window.clearTimeout(hbl.client.pollingmanager.last_poll_timer);
         
         // cleanup the call from the stack to re-run queued items
         hbl.client.jsoncallback({},null);

         // if no new item was fired, re-fire get_messages
         if(hbl.client.last_getmessages.getTime() - now < 0){
           hbl.client.get_messages();
         }
       }
       
       hbl.client.pollingmanager.check_for_failed_timer = setTimeout("hbl.client.pollingmanager.check_failed_polling()", hbl.client.pollingmanager.check_failed_polling_rate );
     }
     
   },
   
   
   /* site ID and wc_id */
   callbacks: {
     begin: function(r) { hbl.client.jsoncallback(r,hbl.client.begin_call_back); },
     sendmessage : function(r) { hbl.client.msg_callback(r); },
     
     // start get messages
     getmessages : function(r) { hbl.client.pollingmanager.finished_last_poll(); hbl.client.jsoncallback(r, hbl.client.callbacks.pollmessages1); },
     pollmessages : function(r) { hbl.client.pollingmanager.finished_last_poll(); hbl.client.jsoncallback(r, hbl.client.callbacks.pollmessages1); },
     
     pollmessages1 : function(r) {
       try {
         // Ok, I need to fire an OpStatus Change event if the opstatus is different
         if(r.opavailable != hbl.client.opavailable || hbl.client.opmessage  != r.opmessage || hbl.client.opstatus != r.opstatus || hbl.client.opbusy != r.opbusy) {
           // change the op status
           hbl.client.opavailable = r.opavailable;
           hbl.client.opmessage   = r.opmessage;
           hbl.client.opstatus    = r.opstatus;
           hbl.client.opbusy      = r.opbusy;   
           /* now with a callback */
           // fire an event
           hbl.client.eventmgr.handle("operator_status_change", { "available": r.opavailable, "status": r.opstatus, "message": r.opmessage, "busy" : r.opbusy } );
         }  
         
         hbl.client.append1( r.buffer );

         // Start the next poll here
         // if they got content raction() will be set, otherwise.. just be normal.
         hbl.client.pollingmanager.start_next_poll(  r.opavailable, r.opbusy ,hbl.client.config.vars["poll"], (hbl.client.config.vars["start_passive"] && r['passive_mode'])  );
         
         /*
           Also in the future the JSON call could control how long this call is, etc..
         */
         
         // I need to figure out how to deal with the case where the window is invisible
         // do we keep chatting, do we make the timeout higher..
         // what the heck do we do?
       } catch(e) {
         /* do something here */
         throw e;
       }

     
     
     },
     setnickname : function(r) {}
   },
   
   siteid: "",
   wc_sid: "",
   sid : "",   
   proxy : null,
   // Temporary timeout variables for now.
   offline_timeout : 55*1000,
   offline_timeout_default : 55*1000,
   offline_timeout_slow : 150*1000,
             
   online_timeout  : 20*1000,
   chatting_timeout : 700,
   current_timeout : 20*1000,
   first_timeout : 800,
   no_chat_timeout : 40 * 1000,
   last_msg_time: new Date(),
   
   /** more timeout vars **/
   check_get_message_timeout : 20 * 1000,
   fast_polling : false,
   no_chat_timeout_slow: 5 * 60 * 1000,
   online_poll_slow:  120 *1000,
   
   /** name for local user **/
   myname : "you",
   
   /** general state variables **/
   opstatus    : null,
   opavailable : null,
   opmessage   : null,
   chatting : false,
   buffer: new Array(),
   
   /** variables for calling the JSON stuff **/
   the_count : 0,
   newlinecount : 0,
   lastindex : 0,
   last_getmessages : new Date(),

   
   /*** json code ***/
   jsondata : null,
   jsonthrottle_locked : false,
   datareadycallback : null,
   getmsgcallback : null,
  
   /*
     This function is called when we get a value back from the backend
     
   */
   jsoncallback : function(data , callback) {
     if(data["error"]) {
       // ok we got an error.. call begin again and retry
       //return;
     }

     if( callback!=null ) {
       hbl.util.debug(" => returned drcb to null");
       callback(data);
     }
     
     hbl.client.jsonthrottle_locked = false;
     if( hbl.client.proxy.callq.length>0 ) {
         var tmp2 = hbl.client.proxy.callq.shift();
         hbl.client.proxy.docall( tmp2[0], tmp2[1], tmp2[2] , true);
     }
   },


   begin_call_back : function(r) {
     /* check if there is a free slot for us first */
     if( r!=null && !r["error"]) {
       hbl.client.sid = r.sid; // set sessionID
       hbl.client.wcsid = r.sid;
       hbl.client.hblid = r.hblid; // set habla user ID
       
 
       /* now with a callback */
       /* ADD EVENT MANAGER */
       // I should probably also pass whether the window should be open or not here as well :-)
       hbl.client.eventmgr.handle("chat_started", { 
                                                   "chatting": (r.chatting=="on"), 
                                                   "box_open" : (r.box_open == "on"), 
                                                   "nick" : r.nick, 
                                                   "box_visible" : (r.box_visible == "on"),
                                                   // Here on is to handle away status
                                                   "opstatus"       : r['opstatus'],
                                                   "opavailable"    : r['opavailable'],
                                                   "opmessage"      : r['opmessage'],
                                                   "opbusy"         : r['opbusy'],
                                                   "config"         : r['config'],
                                                   "dconfig"        : r['dconfig'],
                                                   "habla_message"  : r['habla_message']
                                                   } );

       if( r.context ) {
         // I might want to pull this out into an EVENT handler
         hbl.client.setContents( r.context );
       }
       // you could imagine moving the setCookie somewhere else 
       // to make this more scalable
       
       if( hbl.client.config && ! hbl.client.config.vars["disable_set_cookies"]) {
         hbl.util.set_cookie( "wcsid", hbl.client.sid );   // set the SESSIONID
       }
       hbl.util.set_cookie( "hblid", hbl.client.hblid, 24*60*60*1000*356 ); // Have the Hab.la ID last forever :-)

       // let's wait a little bit before getting a session: (I'll always need to make this call)           
       hbl.client.pollingmanager.start_next_poll(  r.opavailable, r.opbusy, hbl.client.config.vars["poll"], ( hbl.client.config.vars["start_passive"] && (r.chatting!="on") ), true  );
       
       /* we only need to check for getmessages freezing if we're sleeping.
        * actually, I hate this whole thing. do we need it? */


       /*
         Resend old messages if necessary
       */
       if( r.opavailable ) {
         // resend the old messages
         for(i=0; i < hbl.client.messageq.length; ++i) {
           // resend all these messages
           hbl.client.proxy.sendmessage( hbl.client.sid, hbl.client.messageq[i] , hbl.client.msg_callback);
         }
       }
       
     }else if (r && r["error"]) {
         // Odds are the SITE-ID is invalid
         hbl.client.eventmgr.handle("habla_error", {'etype' : r["error"]});  
     }
      
      
   },

   /*
    *
    * Ok, we will be passed the:
    *
    * wc_sid  -- session ID
    * hbl_id  -- hblID of the user 
    * site_id -- ID of the site
    * eventmgr -- 
    * config (standard Hab.la config object)  
    *
    * it will then pass HBLSID= to all URLs
    */
   start : function( wc_sid, hbl_id,site_id,  eventmgr, config){
     /* This is going to start the client*/
     hbl.client.siteid = site_id;
     hbl.client.wcsid  = wc_sid;
     
     // only set the Hab.la ID if it's not already set?
     if(hbl.client.hblid == undefined) 
       hbl.client.hblid  = hbl_id;
     
     // I need failsafes incase these are nil
     hbl.client.eventmgr = eventmgr;
     hbl.client.config = config;
     
     hbl.client.event_driven = false;
     
     /*
       So.. this raises a question, should the SERVER parse text sent from the client side.
       or should we do it here..
       
       OR should we just do both :-)
       both seems more developer friendly.
     
     */
     hbl.client.incoming_pipeline.setTarget(this);

     try {
       if( hblrpcTrick) {
         // Ok, if we are doing the trick, we'll have two proxies, one will be 'random'
           hbl.client.proxy = new hbl.client.jsonproxy( "aync." + hbl_hostname, true);
           var first_part = parseInt(Math.random() * 1000).toString() + ".event.";
           hbl.client.proxy2 = new hbl.client.jsonproxy( first_part + hbl_hostname);
            
       }else {
           hbl.client.proxy = new hbl.client.jsonproxy( hbl_hostname);
           hbl.client.proxy2 = hbl.client.proxy;
       }
       if(hbl.config && hbl.config.vars && !hbl.config.vars['local_mode']) {
         hbl.client.proxy.begin(hbl.client.wcsid, hbl.client.hblid, hbl.client.siteid , document.URL, document.referrer, config.vars["start_passive"], config.vars["force_nickname"], hbl.client.begin_call_back);
       }
     } catch(e){
     
       hbl.client.chatting = false;
       hbl.util.reportException(e);
       throw e;
     }
     
   },
   
   get_messages : function(force_poll) {
     var r;
     hbl.util.debug("Got to get_messages");
     try {
       /* ideally we should clean this code up a bit.. */
       if( force_poll != undefined || hbl.client.config.vars["poll"] || hbl.client.pollingmanager.current_poll_rate != 0  ) {
         hbl.client.proxy.pollmessages(hbl.client.sid, hbl.client.lastindex, hbl.client.callbacks.pollmessages);
       } else {
         /* send messages in the queue first, then get incoming messages */
         hbl.client.proxy2.getmessages(hbl.client.sid, hbl.client.lastindex, hbl.client.callbacks.getmessages);
       }
     } catch(e) {
       hbl.util.reportException(e);
       //hbl.wc_hide_window();
       hbl.client.chatting = false;
       throw e;
       throw "getmessage failed";
     }

     return false;
   },
       
   
   /** ---------------------------------------------------------------------------------------------------------------------**/
   
   jsoncallback_norv: function(data) {
     // We should probably add some retun value processing
   },

   jsonproxy : function(uri, rnd) {
     this.uri = uri;
     this.callq = new Array();
     this.rnd = rnd;
   },
     
   check_getmsg : function() {

   },  
   
   /** These are all utility functions that could probably be combined into one function **/
   
   setContents : function(cont) {
     if( !cont || cont.length == 0) return;
     
     hbl.client.lastindex = 0;
     for(i=0;i<cont.length;++i) {
       hbl.client.buffer[hbl.client.buffer.length] = new Array(cont[i][0], cont[i][1]);
       if( cont[i][2]>hbl.client.lastindex )
         hbl.client.lastindex = cont[i][2];
     }
     // I need some sort of call back here
     // like .. look we got more content
     // do something now.
     hbl.client.newlinecount = 1;
     //hbl.util.debug("Buffer Length is: ");
     //hbl.util.debug(hbl.client.buffer.length);
     
     // We got some sort of ACTION if we are here.
     hbl.client.pollingmanager.raction();
     

     
     /* ADD HANDLER HERE*/
     hbl.client.eventmgr.handle("receive_message", { "type" : "start" , 'msg' : cont} );
   },
 
   /* this is used to append outgoing message to the chat */
   append : function(buf) {
     if( !buf || !(buf.length>0) ) return;
          
     for(i=0; i < buf.length; ++i) {
       // If I wanted to turn links into links
       // right here would be one place to do it.
       hbl.client.buffer[hbl.client.buffer.length] =  new Array(buf[i][0], buf[i][1] );
       //hbl.client.lastindex = buf[i][2];
     }
     hbl.client.newlinecount += buf.length;
 
     // We got some sort of ACTION if we are here. i.e. sent a message
     hbl.client.pollingmanager.raction();
     hbl.client.pollingmanager.saction();
     
     // I need to know if they are available or not [ I am sure I store this somewhere]
     hbl.client.pollingmanager.start_next_poll( hbl.client.opavailable  , hbl.client.opbusy, hbl.client.config.vars["poll"], false );
     
     // fire an event noting an update
     hbl.client.eventmgr.handle("send_message", { "type" : "local_update" , "msg" : buf} );
   },

   /* this is used when receiivng a message */
   append1 : function(buf) {
     if( !buf || !(buf.length>0) ) return;

     var message_array = [];
     var added = 0;		
     for(i=0; i < buf.length; ++i) {
       // If I wanted to turn links into links
       // right here would be one place to do it.
       /*
         Preprocess :-)
       */
       if( buf[i][2] > hbl.client.lastindex ){ // Make sure we don't put duplicate messages in.
         var msg = hbl.client.incoming_pipeline.run(buf[i]);
       
         if( msg != undefined ) {  
         
           hbl.client.buffer[hbl.client.buffer.length] = msg;
           hbl.client.lastindex = msg[2];
           
           message_array.push(buf[i]); // add 
	      }
         if(buf[i])
	          added +=1; // even if the pipeline spits out NONE we'll call it working, if they get 'something'
       } // end this lastindex javascript check
       else {
         hbl.util.debug("GOT A DUPLICATE MESSAGE ID BACK:");
       }
     }
     hbl.client.newlinecount += buf.length;
     
     // Should this be a plugin?
     // We got some sort of ACTION if we are here.
     
     if(added > 0) {
        hbl.client.pollingmanager.raction();
     }

     // Fire an update
     // fire an event noting an update
     hbl.client.eventmgr.handle("receive_message", { "type" : "remote_update" , "msg" : message_array} );
   },

   
   /** end functions that should be combined into one **/
   messageq : [],
   msg_callback: function(data) {
     hbl.util.debug("Send message got:" + data);
     if(data["error"] == "no_site_id") {
       // ok if there's an error I should do some stuff
       hbl.util.debug("Got an error");

       // Essentially.. just re-call begin, and then call sendmessage again
       hbl.client.proxy.begin(hbl.client.wcsid, hbl.client.hblid, hbl.client.siteid , document.URL, document.referrer, hbl.client.config.vars["start_passive"], hbl.client.config.vars["force_nickname"], hbl.client.begin_call_back, false);

     }else if(data["error"] == 'operator_is_busy'){
      /* hmm.. the operator is busy */
      /* now what */
      hbl.client.eventmgr.handle("habla_error", { "etype" : 'operator_is_busy'} );
     }else {
       hbl.client.messageq.shift(); 
     }
      
   },
   sendmessage : function(msg) {
     hbl.util.debug("chatclient.send " + msg);
     var r;
     try {
         hbl.client.messageq.push(msg);

         var tmp = new Array(new Array(hbl.client.myname,msg) );
         hbl.client.append(tmp);
         hbl.client.proxy.sendmessage( hbl.client.sid, tmp[0][1] , hbl.client.msg_callback);
       } catch( e ) {
         hbl.util.debug("EXCEPTION:" + e);
         throw e;
       }
   },

   setnickname : function(nick) {
     hbl.util.debug("chatclient.setnickname " + nick);
     var r;
     try {
       hbl.client.proxy.setnickname( hbl.client.sid, nick );
     } catch( e ) {
       hbl.util.debug("EXCEPTION:" + e);
       throw e;
     }
   },
   
   /** Essentially logging functions -- right now -- should just have a SENDEVENT**/
   sendexpand : function() {
     if( ! hbl.client.proxy) return; 
      hbl.client.proxy.expandchat(hbl.client.sid);
   },
   sendcompress : function() {
     if( ! hbl.client.proxy) return; 
      hbl.client.proxy.compresschat(hbl.client.sid);  
     //hbl.client.proxy.shrinkchat(hbl.client.sid);
   },
   sendend : function() {
     if( ! hbl.client.proxy) return; 
     hbl.client.proxy.endchat(hbl.client.sid);
   },
   sendhide : function() {
     if( ! hbl.client.proxy) return; 
     hbl.client.proxy.hidechat(hbl.client.sid);
   },
   sendshow : function() {
     if( ! hbl.client.proxy) return; 
     hbl.client.proxy.showchat(hbl.client.sid);
   }
   /* 
     Should add some simple way of setting text color.. etc..
   */
   
   
   /** end the logging stuff **/
     
 };

//-------------------------------------------------------

hbl.client.jsonproxy.prototype.docall = function(fn, args, cb, throttle) {
  // Around here is where we could add a hack to send some identifier to make sure they are using the right API (i..e the javascript)
  
  /* an ugly, but necessary hack */
  if(hbl && hbl.config && hbl.config.vars['local_mode'] ) return;
  
  if( throttle ) {
    if( hbl.client.jsonthrottle_locked ) {
      
      // I only throttle get_messages and polling as far as I know?
      // we really can probably just limit the length to 2 calls otherwise.. 
      // it might get crazy.
      if(this.callq.length < 2)
        this.callq[this.callq.length] = new Array(fn, args, cb);
      
      return;
    }
  
  } 
  var oururi = '';
  var ret = '';
  
  if(this.rnd) oururi = parseInt(Math.random() * 10).toString() + ".";
  
  oururi += this.uri + "/" + fn + "?"; 
  for( i=0; i < args.length; ++i ) {
    if( i>0 ) oururi += "&";
    ret = '';
    if( args[i]) {
      ret = encodeURI(args[i] + "").replace(/\#/g,"%23").replace(/\&/g,"%26");
      if(i ==0)
        ret = "HBLSID=" + ret;
      // append it
      oururi += ret;
    }
  }
  
  /* the rpc server returns data as a javascript function call */
  /* so to execute the call, we just include the URI as javascript! */
  /* neat, huh? */
  if( throttle ) {
    hbl.client.jsonthrottle_locked = true;
  } 
  // Util function to load a remote URI
  hbl.util.load_js_async("http://" + oururi);

  /* we don't have to do an onload event; we are already running a callback */
  return null;
};
// We need to add more random number to all the args so we can send more messages, etc.

/* gotta be a better way to do this?  (We need to incorporate REF which is the page their referring URL)*/  
hbl.client.jsonproxy.prototype.begin = function(sid, hblid, siteid, page, ref,  start_passive, nickname, cb, throttle) {
  if(throttle == undefined) {
    throttle = true;
  }
  hbl.util.debug("Throttle is: " + throttle);
  var rnd = hblJavaScriptVersion + "-" + Math.random().toString();
  if(nickname) {  
    return this.docall(hblBegincall, new Array(sid, hblid, siteid, page, rnd, ref, start_passive, nickname), cb, throttle);
  }else{
    return this.docall(hblBegincall, new Array(sid, hblid, siteid, page, rnd, ref, start_passive ), cb, throttle);
  }
};

hbl.client.jsonproxy.prototype.sendmessage = function(sid, msg, cb) {
  var rnd = hblJavaScriptVersion + "-" + Math.random().toString();
  return this.docall("sendmessage", new Array(sid, msg, rnd), cb , false);
};

hbl.client.jsonproxy.prototype.setnickname = function(sid, nick, cb) {
  return this.docall("setnickname", new Array(sid, nick), cb ,false );
};

hbl.client.jsonproxy.prototype.getmessages = function(sid, idx, cb) {
  hbl.client.the_count++;
  hbl.client.last_getmessages = new Date();
  return this.docall("getmessages", new Array(sid, idx.toString(), hblJavaScriptVersion + "-" + (hbl.client.the_count + Math.random()).toString()), cb, true);
};

hbl.client.jsonproxy.prototype.pollmessages = function(sid, idx, cb) {
  hbl.client.the_count++;
  return this.docall("pollmessages", new Array(sid, idx.toString(), hblJavaScriptVersion + "-" + (hbl.client.the_count + Math.random()).toString()), cb, true);
};


/** new functions **/
hbl.client.jsonproxy.prototype.expandchat = function(sid, cb) {
  return this.docall("expandchat", new Array(sid, hblJavaScriptVersion + "-" + Math.random().toString()), cb);
};

hbl.client.jsonproxy.prototype.compresschat = function(sid, cb) {
  return this.docall("compresschat", new Array(sid, hblJavaScriptVersion + "-" + Math.random().toString()), cb);
};

// End the chat entirely
hbl.client.jsonproxy.prototype.endchat = function(sid, cb) {
  return this.docall("endchat", new Array(sid, hblJavaScriptVersion + "-" + Math.random().toString()), cb);
};

// keep track of whether the chat is hidden or shown
hbl.client.jsonproxy.prototype.showchat = function(sid, cb) {
  return this.docall("showchat", new Array(sid, hblJavaScriptVersion + "-" + Math.random().toString()), cb);
};

hbl.client.jsonproxy.prototype.hidechat = function(sid, cb) {
  return this.docall("hidechat", new Array(sid, hblJavaScriptVersion + "-" + Math.random().toString()), cb);
};
/** End JSON Proxy portion of the client**/

// Determine Window Status:

// A util function so I can use RETURNs



// I can definitely have a better callback than this. end on this.
// hbl.util.loaded("client.js");
/*
 TODO:
 
 Break this into two pieces
 
 one piece that is sort of a wrapper for a Theme
 
 and the 'theme'
 
 possibly have some elements of a STATE Machine
 
 Wrapper will:
    -- handle external events
    -- render theme
    -- expand/close theme

  Also need something to handle config and overwriting settings, etc.


 */






/* the default theme */


/*
 * So that is a Template (more or less )  
 * now I need to put it inside of a container.
 * that does most of the real work
 */

hbl._hwindow = function(client, config, eventmgr) {
  
  this.config = config;
  this.client = client;
  this.handlers = false;
  this.theme    = undefined;
  this.eventmgr = eventmgr;
  this.loaded   = false;
  
  this.expanded_textbox = false; /* set to true when textbox is expanded */
  
  this.setTheme = function(theme) {
    if(this.theme)
      this.theme.remove();
      
    this.theme = theme;
    this.theme.build(this.config, this.client);
    
    // Do the post theme configured tasks:
    // only load the plugins and IE HACK one time.
    if(!this.loaded ) {
      this.loaded = true;
    
      // might just want to make this bit smarter
      // render the hacks for IE if needed
      if(hbl.util.BrowserDetect.backwards_position) {
        hbl.config.render_ie_hacks();
      }
    }
    
    /* 
        Double check the theme to make sure we have a hab.la link div 
        and the div contains a link to Hab.la and is not hidden.
    
    */    
    this.check_theme_for_link_div();
    
    // Fire the plugin loader  
    hbl.util.pluginloader(this.config, this.client, this); /* hmm.. */ 
    
  };
  
  this.check_theme_for_link_div = function() { 
    if(! this.theme) return;
    
    if(! this.theme.habla_link_div || (this.theme.habla_link_div.style && this.theme.habla_link_div.style.display == "none") ||
    this.theme.habla_link_div.innerHTML.indexOf("http://hab.la") < 0 || ! this.theme.habla_link_div.parentNode ) {
      /* a few of the simple cases I figured I could check.. we don't check for not being appended oh well */
      
      this.theme.habla_link_div = document.createElement("div");
      if(this.config.vars["force_name_habla_link_div"] ) {
        this.theme.habla_link_div.setAttribute("id","habla_link_div");          
      } 
      /* 
         otherwise maybe I should just leave it anonymous -- really I need to move this code into Window, so people don't 
         customize it away

      */

      /* this should only happen when I set a specific variable, i.e. most of the time I should randomize the ID here */
      this.theme.habla_link_div.innerHTML = this.config.vars['special_link'];

      if(this.theme.config.vars["append_middle"]) {
        this.theme.habla_middle_div.appendChild(this.theme.habla_link_div);
      }else {
        this.theme.habla_expanded_div.appendChild(this.theme.habla_link_div);
      }
      
    };

    /* double check the value */
    if(!document.getElementById('hblink1')){
      this.theme.habla_link_div.innerHTML = 'Free chat by <a href="http://hab.la" id="hblink1" target="_blank">Hab.la</a>';
    }
    // Re-render
    this.config.render_js_style(document.getElementById('hblink1') , "habla_link_a");
    this.config.render_js_style(document.getElementById('hblink2') , "habla_link_a");
    
    /* render the link div */
    this.config.render_element_id(this.theme, "habla_link_div");
    
  };
  
  
  /* if we have a theme defined in the config, load it here. */

  
  
  
  this.register_handlers = function(){
    if(this.handlers) return;
    this.handlers = true;
    
    /* list out a bunch of handlers that this needs to handle */
    
    /*UI handlers */
/*
    window_closebutt_clicked
    window_minbutt_clicked
    window_topbar_clicked
    window_oplink_clicked
    window_focus
    window_submit
    window_form_submit
*/  
    /* window events */
    this.eventmgr.register("window_focus", this.events.onWindowFocus ,0 );
    this.eventmgr.register("window_click", this.events.onWindowFocus ,0 );
    this.eventmgr.register("window_submit", this.events.onWindowSubmit ,0 );
    this.eventmgr.register("window_form_submit", this.events.onWindowFormSubmit ,0 );

    /* click events */
    this.eventmgr.register("window_topbar_clicked", this.events.topBarClicked ,0 );
    this.eventmgr.register("window_closebutt_clicked", this.events.closeClicked ,0 );
    this.eventmgr.register("window_sizebutt_clicked", this.events.topBarClicked ,0 );
    this.eventmgr.register("window_oplink_clicked", this.events.topBarClicked ,0 );
    
    /*other event handlers: */
    /*
    operator_status_change
    chat_started
    "receive_message", { "type" : "start" , 'msg' : cont}
    "send_message", { "type" : "local_update" , "msg" : buf} );
    "receive_message", { "type" : "remote_update"
    */
    
    /* chat status events */
    this.eventmgr.register("operator_status_change",this.events.onOpstatusStatusChanged ,0 );
    this.eventmgr.register("receive_message", this.events.onChatUpdated ,0 );
    this.eventmgr.register("send_message", this.events.onChatUpdated ,0 );
    this.eventmgr.register("chat_started", this.events.onChatStarted ,0 );
    
    this.eventmgr.register("theme_loaded", this.events.onThemeLoaded, 0);
  };
  
  
  /* -----------------------------------------------------------------------------------------------------------------------------*/
  /* util functions */
  
  this.get_operator_state = function() {
    // returns the operator state
    // given:
    // this.opavailable
    // this.opstatus
    // this.opbusy
    // this.opmessage
    if(this.config.vars["local_mode"]) {
      return (this.config.vars["local_operator_state"] || "available");
    }
    if(this.opavailable) {

      if(this.opbusy) {
        return "busy";
      }else {
        if(this.opstatus == "chat" || !this.opstatus || this.opstatus == "") {
          return "available";
        }else {
          return "away";
        }
      }
    }else {
      return "notavailable";
    }
  
  };
  // another status function
  
  this.determine_window_status = function(arg) {  
    // if we are chatting ,we are going to show the window.
    if(arg["chatting"])
      return true;

    if(arg["box_open"] || arg["window"].config.vars["start_expanded"]) {
      return true;
    }

    if( arg["window"].config.vars["start_hidden"] && !arg["window"].theme.visible ){ /* I don't think this helps*/
      // hide it if we want to start it hidden
      return false;
    }

    // Hide it if op is not available, and hide_not_available
    if( arg["window"].config.vars["hide_not_available"] && ! arg["opavailable"] ) {
      return false;
    }

    // hide when not chatting not "" and not undefined
    if( arg["window"].config.vars["hide_when_away"] &&  (arg["opstatus"]  != "chat" && arg["opstatus"] != undefined && arg["opstatus"] != "")  ) {
      return false;
    }

    return true;
  };
  
  
  
  
  // I need that reload Buffer
  
  this.setWindowText = function() {
    var opstate = this.get_operator_state();
    
    
    if(opstate == "notavailable") {
      this.setHeader( this.config.vars["not_available_text"] );
      this.setBody(   this.config.vars["offline_message"] );
	  document.getElementById("s5_online").style.display = "none";
	  document.getElementById("s5_offline").style.display = "block";
      return; 
    }

    if( opstate == "away" ) {
      // set the header to away
	  
	  document.getElementById("s5_online").style.display = "none";
	  document.getElementById("s5_offline").style.display = "block";

      if(this.config.vars["show_away_as_header"] && this.opmessage) {
          this.setHeader ( this.opmessage , true);      // setHeader function, to have it auto substring.
        }else {
          this.setHeader( this.config.vars["away_text"]);
        }

      // no chat yet. 
      if( this.client.buffer.length == 0 ) {
        // right now AWAY will be treated the same as offline.
        // I may want to change this down the line.!
        this.setBody( this.config.vars["away_message"] || this.config.vars["offline_message"]  );  
      }
      
      return;
    } // end AWAY state

    if(opstate == "busy") {
      this.setHeader( this.config.vars["busy_text"] );
      this.setBody( this.config.vars["busy_message"] );
	  document.getElementById("s5_online").style.display = "none";
	  document.getElementById("s5_offline").style.display = "block";
      return;
    }

    if( this.client.buffer.length == 0 ) {
	  document.getElementById("s5_online").style.display = "block";
	  document.getElementById("s5_offline").style.display = "none";
     // welcome message
     // and pre-chat
     this.setBody( this.config.vars["welcome_msg"] );
     
     if(this.expanded) {
       this.setHeader( this.config.vars["in_chat_text"]);       
     }else {
       this.setHeader( this.config.vars["before_chat_text"]);
     }
    }else {
      // now chatting
      this.setHeader( this.config.vars["in_chat_text"]);
    }

  }; // end setting the conversation / header blurb
  
  
  this.appendFromBuffer = function(buffer) {
    if(!this.theme) {
      /* most of the time, this will occur */
      hbl.hwindow_last_buffer = buffer;
      this.eventmgr.register("theme_loaded", 
        function(arg) {
          arg["window"].appendFromBuffer(hbl.hwindow_last_buffer); 
          hbl.hwindow_last_buffer ="";}
      , 100);
      
    }else {
    
      for(i in buffer) {
        // format the message -- pipeline style
        this.theme.appendNiceMessage(buffer[i][0], buffer[i][1], true /* is the operator online? */ );
      }  
    }
  };
  
  /* -----------------------------------------------------------------------------------------------------------------------------*/
  // Normal Use functions
  
  this.setHeader = function(txt, sub) {
    if(sub) {
      txt = ".." + txt.substr(0,10) + ".."; // this number will also be based on the width.
    }
    this.theme.setHeader(txt);

  };
  

  /* -----------------------------------------------------------------------------------------------------------------------------*/
  // EVENTS
  
  /* The handlers */
  this.events = {
    
    onOpstatusStatusChanged : function(arg){
      
      // Setup the client STATE:
      arg["window"].opstatus  =  arg["status"];
      arg["window"].opmessage =  arg["message"];
      arg["window"].opbusy    =  arg["busy"];
      
      if(! arg['window'].opavailable && arg["available"]){ 
        // reload the buffer if it didn't use to be available, but now it is.
        arg['window'].reloadBuffer();
      }
      
      arg["window"].opavailable = arg["available"];

      // set the window text  
      arg["window"].setWindowText();
      
      // Should we show the window:
      var show = arg["window"].determine_window_status(arg);

      if(show || arg["window"].theme.visible ) { /* we don't hide windows that are open */
        arg["window"].show();  /* let's store the status */
      }else {
        arg["window"].hide(1);
      }
        
    },
    
    onChatUpdated : function(arg){
      // will get this for sending or receiving a message
      // or setting up messages for the first time.
      //arg['msg'] // is an array.
                 // I am not convinced we treat them differently.. but I am not sure.. at least 
                 // not right here?
      
      arg["window"].appendFromBuffer(arg['msg']);  
    },
    
    // so much cleaner than before :-)
    onChatStarted : function(arg){
      arg["window"].opavailable =  arg["opavailable"];
      arg["window"].opbusy      =  arg["busy"];
      
      // load config from server
      if(arg["config"]) {
        hbl.util.debug("merging config");
        arg["window"].config.merge(hbl.util.decode_base64(arg["config"], true));
      }
      // load the default config options from server
      if(arg["dconfig"]) {
        hbl.util.debug("merging config");
        arg["window"].config.merge(hbl.util.decode_base64(arg["dconfig"],true));
      }
      
      if( arg["nick"] != null && ! arg["nick"].match(/webuser\d\d/) ) {
        // This is essentially updating roland's frame -- huh?.
        hbl.util.debug("nickname is: " + arg["nick"]);
        arg["window"].setnickname(arg["nick"], 1);
      }
      
      // Right here is probably where we need to 
      // see if we have a theme... and then act accordingly.
      // i.e. we need to LOAD it.
      // and after loading it. get moving.
      hbl.chat_started_arg = arg;
      
      if(arg["window"].theme) {
        arg["window"].events.onThemeLoaded(arg);
      }else {
        if(arg['window'].config.vars["theme_obj"]) {
          arg['window'].setTheme(config.vars["theme_obj"]);
          arg["window"].events.onThemeLoaded(arg);
        }else { 
        // this is kind of a hack
          hbl.chat_started_arg = arg;
          var to_load = "";
          if( arg['window'].config.vars["theme_path"] )
            to_load = "http://" + hbl_static_hostname + "/" + arg['window'].config.vars["theme_path"];
        
          if( arg['window'].config.vars["theme_url"] )
            to_load = arg['window'].config.vars["theme_url"];
        
          // load the theme here.
          hbl.util.simple_load_js_async(to_load);
        }
      }
      
    },
    /*
     * This function is becoming the.. GET EVERYTHING GOING FUNCTION
     * 
     * I guess this isn't that big of a deal.. but.. eh..
     */
    onThemeLoaded : function(ar) {
      
      //set up the theme:
      if(!ar["window"].theme) ar["window"].setTheme(ar['obj']);
      
      
      
      // Get the last begin calls.. state
      var arg = hbl.chat_started_arg;
      
      var show = arg["window"].determine_window_status(arg);

      if(show) {
        arg["window"].show(1);
      }else {
        arg["window"].hide(1);
      }




      if(arg["box_open"] || arg["window"].config.vars["start_expanded"]) {
          // toss this into the expand call.
          arg["window"].expand();
      }
      
      /* the special case of the Habla_message */
      if(arg["habla_message"]) {
        
        arg["window"].config.vars["offline_message"] = arg["habla_message"]["msg"];
        
        if(arg["habla_message"]["header"] ) {
          arg["window"].config.vars["not_available_text"] = arg["habla_message"]["header"];
          arg["window"].setHeader( habla_window.config.vars["not_available_text"]);
        }
        
        arg["window"].show(1);
        
        if(arg["habla_message"]["expand"]) {
          arg["window"].expand();
        }
        arg["window"].theme.habla_conversation_div.scrollTop = 0;
      }
      
    },
    
    // another handler here
    onWindowFocus : function(arg) {
        hbl.util.debug("focused");
        // set the window text  
        arg["window"].normal();
        arg["window"].setWindowText();
    },
    
    /*
      This event does two things.
      
      resets the window.. and expands/ contracts it.
      
      * it may only need to expand/contract it now.
     */
    topBarClicked  : function(arg) {
      // I need a double click test
      // I need to make Throttle a little bit nicer.. and a lot smarter
      hbl.util.debug("topBarClicked " + arg["window"].expanded);
      if(hbl.util.doThrottle() )  return false;
      hbl.util.debug("made it past throttle");
      //we'll take it as focus
      arg["window"].normal();

      // Checked if expanded
      if(arg["window"].expanded) {
        arg["window"].compress();
      }else {
        arg["window"].expand();
      }
      
      return false;
    },
    
    closeClicked : function(arg) {
      // set the throttle
     if(hbl.util.doThrottle() )  return false;
      hbl.util.debug("CLOSED CLICKED");

      if(arg["window"].expanded) {
        arg["window"].compress();
      }else {
        arg["window"].close();
        // because this is a somewhat special case.. we'll put this here
        /*
          
          
          // need to set chatting to false.. in the client.. but this should
          // all be rolled into the actual window.. not the event
          
        */
      }
      // should I do other things?
      return false;
    },
    onWindowFormSubmit : function(arg) {
      arg["window"].send();
    },
    onWindowSubmit : function(arg) {
      var e = arg["event"];

      // Maybe fire some sort of event handler if it's registered
      //var e = event;
      var keynum;
      /* IE/fox difference again */
      if( window.event )  {
        e = window.event;
        hbl.util.debug(e);
        keynum = e.keyCode;
      } else if( e.which ) {
        keynum = e.which;
      }
      /* 13 == enter key */
      if( keynum==13 ) {
        arg["window"].send();
      }
      /* check the length, and resize if necessary. */
      if(! arg["window"].config.vars["disable_expand_text_input"] && ! arg["window"].expanded_textbox && arg["window"].theme.getMessageInputFieldValue().length > arg["window"].config.vars["resize_length"] ){
        // resize it
        arg["window"].expanded_textbox = true;
        arg["window"].setInputHeight(arg["window"].config.vars["resize_input_height"] );
      }
      //setInputHeight back to small again
      if(! arg["window"].config.vars["disable_expand_text_input"] && arg["window"].expanded_textbox && arg["window"].theme.getMessageInputFieldValue().length < arg["window"].config.vars["resize_length"] ){
        // resize it
        arg["window"].expanded_textbox = false;
        arg["window"].setInputHeight(arg["window"].config.vars["input_height"] );
      }
      
      
    }


  };
 
  // event list
  //alert("above events");

  /* -----------------------------------------------------------------------------------------------------------------------------*/
  this.send_pipeline = new hbl.util.pipeline(this);
  this.send_pipeline.add(hbl.pipelines.nickname,999);

  return this;
}; 

// end this whole block






/*
// need to roll these into event handlers.

 */

/*************************************************

Ok the next step is to.. 

* add the event handlers back in.

* break the template out from the Window.  (I guess we could imagine people having custom Windows.. 
  if they wanted to do more, than what you can do with a template? )

* Solve the config/overwite problem.

* incorporate pipelines back in? -- where relevant.
* figure out how to theme incoming messages?

* figure out how to handle various status information. etc... 


*/






/** -------------------------------- **/
hbl._hwindow.prototype.setnickname = function(nick, local) {
  // This should update it in the window
  
  // Set the nickname here :-)
  if(local == undefined) {
    this.client.setnickname(nickname);
  }
  // strange?
  // seems like I'd need more here
};

hbl._hwindow.prototype.show = function(local_only) {
  
  this.theme.show();
  this.visible = true;
  
  // You'd want to add a SEND SHOW here
  if(!local_only) this.client.sendshow();
  
  // I hate IE
  hbl.eventmgr.handle("habla_window_changed", {'type' : 'show'} );
};


hbl._hwindow.prototype.close = function(local_only) {
  this.theme.close();
  if(!local_only) this.client.sendend();
  
  // slow down the polling:
  // well for now.. F it.. let's just keep it as is.
  
  hbl.eventmgr.handle("habla_window_changed", {'type' : 'close'} );
};


hbl._hwindow.prototype.hide = function(local_only) {
  this.theme.hide();
  
  this.visible = false;
  if(!local_only) this.client.sendhide();

  hbl.eventmgr.handle("habla_window_changed", {'type' : 'hide'} );
};

hbl._hwindow.prototype.setWidth = function(width) {
  this.theme.setWidth(width);
  hbl.eventmgr.handle("habla_window_changed", {'type' : 'resize'} );    
};

hbl._hwindow.prototype.setInputHeight = function(height) {
  // filter for only the #
  this.theme.setInputHeight(height);
  hbl.eventmgr.handle("habla_window_changed", {'type' : 'resize'} );
};

hbl._hwindow.prototype.setHeight = function(height) {
  // At some point we'll need to adjust this to let you set the height of the top bar.. or the bottom.. MAYBE
  this.theme.setHeight(height);
  hbl.eventmgr.handle("habla_window_changed", {'type' : 'resize'} );
};


hbl._hwindow.prototype.compress = function() {
  this.expanded = false;
  this.theme.compress();

  // OK if the browser sucks do this: [this should probably be an event handler for habla_window_changed]
  
  if( hbl.util.BrowserDetect.backwards_position && this.visible && this.config.monitor) {
    // force a resize
    this.config.monitor.moveTo();
  }

  hbl.eventmgr.handle("habla_window_changed", {'type' : 'compress'} );
  this.client.sendcompress();
};

hbl._hwindow.prototype.expand = function() {
  this.expanded = true;
  
  /* hmm.. perhaps we should move before we expand the window.. */
  if(hbl.util.BrowserDetect.backwards_position ) {
    if(this.config.vars["ycorner"] == "bottom") {
      /* this is the case we want to hit */
      //alert("pre_line");
      //alert(this.theme.habla_window_div.style.left.match(/\d\d*/));
      //alert(this.theme.habla_window_div.style.top.match(/\d\d*/));
      //alert(this.config.vars["convo_height"]);
      
      this.theme.setXY(this.theme.habla_window_div.style.left.match(/\d\d*/), this.theme.habla_window_div.style.top.match(/\d\d*/) - this.config.vars["convo_height"]  - this.config.vars["panel_offset"]);
    }
  }
  
  this.theme.expand();
  this.client.sendexpand();
  hbl.eventmgr.handle("habla_window_changed", {'type' : 'expand'} );

};



/** change the window position **/
hbl._hwindow.prototype.setPosition = function(val) {
  this.config.setPosition(val);
  this.config.render_element_id(this.theme, "habla_window_div");
};

/** Status related **/

hbl._hwindow.prototype.getHeader = function() {
  return this.theme.getHeader();
};

hbl._hwindow.prototype.reloadBuffer = function() {
  // essentially this function will clear the contents of the conversation, and then rebuild it
  this.theme.setBody("");
  this.appendFromBuffer(this.client.buffer); 
};

hbl._hwindow.prototype.setBody = function(msg) {
  this.theme.setBody(msg || "");
};




hbl._hwindow.prototype.highlight = function() {
  this.theme.highlight();
};

hbl._hwindow.prototype.normal   = function() {
  this.theme.normal();
};


/* takes the current message buffer and sends it */
hbl._hwindow.prototype.send = function() {
  hbl.util.debug("wc_send");
  var r;
  var buf = this.theme.getMessageInputField();
  var buf_value = this.theme.getMessageInputFieldValue();
  
  if( buf_value && buf_value.length > 0) {
    try {
      var msg = buf_value;
      this.theme.setMessageInputFieldValue("");
      
      /** input pipeline [spam filtering etc..]**/
      msg = this.send_pipeline.run(msg);
      if(msg) {
        this.client.sendmessage(msg);
      }
      
      this.normal();
    } catch( e ) {
      throw e;
    }
  }
  
  buf.focus();
  // Stupid Safari 3 fix.. probably won't hurt, 10 ms is not very long
  setTimeout(function() { habla_window.theme.getMessageInputField().focus(); }, 10);
  
  return false;
};



/** -------------------------------------------------------------------------------------------------------------------- **/
// utility functions for formatting content

/* add newlines to break up any strings that are too wide for the window
str - string to break up
limit - maximum line width
*/

hbl.hwindow_util.wrap = function(str,limit, ignore_url) {
  var first = true;
  
  // The best place to clean pidgin links 
  //str = str.replace(/<http[^>]+>/i, "");

  str = str.replace(/&lt;/i,"[");
  str = str.replace(/\/*&gt;/i,"]");

  var words = str.split(/\s/);
  var rv = '';
  var cur = '';
  var j = 0;
  for(j=0;j<words.length;++j) {
    if( first ) {
      first = false;
    } else {
      rv += ' ';
    }
    cur = words[j];
    
    if(!ignore_url || (ignore_url && !cur.match(/^\[*(https?|ftp|telnet|ldap|irc|nntp|news|irc)/) ) ) {
      while( cur.length > limit ) {
        rv += cur.substr(0,limit-1) + ' ';
        cur = cur.substring(limit-1);
      }
    }
    rv += cur;
  }
  return rv;
};

/*
  Really I should add some sort of "PreDisplay Handler", and have this link + wordwrap be an option for it
  'I guess the link handler is going to be even more complicated :-)'
*/
hbl.hwindow_util.wrap_and_create_links = function(line, hwindow) {
    line = hbl.hwindow_util.wrap(line,21,1);

    // some line here
    // { } -- For some reason ocassionally opera can not parse this line
    // I have no idea why?
    var re = /\b(\[*)(?:((?:https?|ftp|telnet|ldap|irc|nntp|news|irc):\/\/[^\s'"<>()]*|[-\w]+@(?:[-\w]+\.)+[\w]{2,6})\b|([\w\-])+(\.([\w\-])+)*@((([a-zA-Z0-9])+(([\-])+([a-zA-Z0-9])+)*\.)+([a-zA-Z])+(([\-])+([a-zA-Z0-9])+)*)|about:[A-Z0-9._?=%-]{4,19}|[A-Z0-9\_-]*[\.]{0,1}[A-Z0-9\_-]*[\.]{0,1}[A-Z0-9\_-]+\.[A-Z]{2,4})\]*\b/gi;

    line=line.replace(re,function($2, $1) { 
                            var url = $2;
                            var preurl = url;
                            var beeg  = "";	 
                            var ennd  = "";	 
                          
                  			    if($1 != "") {
                      				beeg = "&lt;";
                      				ennd = "&gt;";
                  			    }
                            // I need to see if it's a localURL
                            // or if we should wrap it in a Hab.la frame.
                            url = hbl.hwindow_util.get_habla_url(url, hwindow);
                            
                            return beeg + "<a href=\"" + url +"\" target=\"" + hwindow.config.vars["url_handler_target_window"] + "\" >" + hbl.hwindow_util.wrap(preurl,21) + "</a>" + ennd + " "; 
                            });
    hbl.util.debug(line);
    
    return line;
  };

hbl.hwindow_util.get_habla_url = function(url, hwindow) {
  /* I hate these habla_window references */
  if(!url.match(/\//) && url.match(/^[^\:]+\@/) ) {
    url = "mailto:" + url;
    return url;
  }
  
  if(!url.match(/^(https?|ftp|telnet|ldap|irc|nntp|news|irc|mailto)/) ) {
    url = "http://" + url;
  }
    
  if( hwindow.config.vars["url_handler"] && !url.match(hwindow.config.vars["url_local_pattern"]) ) { 
    return hwindow.config.vars["url_handler"] + "?siteid=" + hbl.siteid + "&wcsid=" + hwindow.client.sid + "&url=" + url;
  }
  return url;
}; 


/** -------------------------------------------------------------------------------------------------------------------- **/

// 


/** -------------------------------------------------------------------------------------------------------------------- **/
/*
  Stub function for all event handlers in the window:
  
  [I can add throttling to the event manager]
*/




hbl.pipelines.nickname = function(msg,hwindow) {
  if( msg.substr(0,6)=="/nick " ) {
    var nickname = msg.substr(6);
    hwindow.client.setnickname(nickname);
    return undefined;
  } 
  
  return msg;
};

hbl.pipelines.wrap_text = function(msg, hwindow) { //999 run after somethings, but before HTMLizing things
  return (hwindow.config.vars["parse_links"] ? hbl.hwindow_util.wrap_and_create_links( msg , hwindow) : hbl.hwindow_util.wrap(msg,21) );
};                                                                                                      /* add something for width */

hbl.pipelines.emoticons = function(msg, hwindow) { //1000 priority (run after wrap_text)
  msg = msg.replace(/\;\-\)/,"<code><big>;-)</big></code>");
  msg = msg.replace(/\:\-\)/,"<code><big>:-)</big></code>");
    
  return msg;
};


/** -------------------------------------------------------------------------------------------------------------------- **/
// for client:

hbl.pipelines.push_url = function(msg, hclient) {
  msg_t = msg;
  msg = msg[1];
  if(msg.substr(0,6)=="!push ") {
    var url = "";
    msg = msg.replace(/&lt;/i,"[");
    msg = msg.replace(/\/*&gt;/i,"]");

    if(msg.match(/<a/)){
      var m = msg.match(/src\s*=\s*"*([^\"]+)\"*/i);
      if(m.length > 1){
        url = m[1];
      }
    }else if(msg.match(/\[(http[^\]]+)\s*\]/i)){ // pidgin fix
      m = msg.match(/\[(http[^\]]+)\]/i);
      url = m[1];		
    }else {
      url = msg.substr(6,msg.length - 6 );
    }
    
    hbl.util.debug("URL IS : " + url);
    if(url != "") {                                         /* there doesn't seem to be a clean way to do this */    
      document.location=hbl.hwindow_util.get_habla_url( url, habla_window );
      return undefined;
    }

  }
  return msg_t;
};


hbl.client.incoming_pipeline.add(hbl.pipelines.push_url,999); // this should be added somewhere else.

/* does this work? -- backwards compatible.. hack? */

/* backwards */


/** -------------------------------------------------------------------------------------------------------------------- **/
/*
  This is where plugins go.
  
  Essentially a plugin let's you load hooks into Hab.la via a nice "clean" interface, so if you want to add a bunch of new event handlers
  or say.. the URL push plugin, or something like that.  It's now trivial to add.
  
  You just need a class.  that implements the method "load(q)"
  q is an array containing elements
  q["hwindow"] -- Habla window
  q["client"]  -- Habla client
  q["conf"]    -- the configuration element
  
*/
/*
  Ok 
*/




/*
  Plugin to notify users of incoming events via visual stimuli
  (no need for a STUB sense we always include this)
  
*/

hbl.plugins.incoming_notification_vars = {};

hbl.plugins.incoming_notification = function () {
  this.name = "incoming_notification";
  
  this.load = function(q) {

    
    hbl.plugins.incoming_notification_vars.freq = 4000;
    hbl.plugins.incoming_notification_vars.flashicon = false;
    hbl.plugins.incoming_notification_vars.flashing = false;
    
    q["hwindow"].eventmgr.register("receive_message", this.onReceiveMessage );
    q["hwindow"].eventmgr.register("window_focus", this.onWindowFocus );
    q["hwindow"].eventmgr.register("window_click", this.onWindowFocus );
    
    
    if( q["hwindow"].config.vars["flash_icons"]) {
      // we are going to flash the icon .. set a var
      hbl.plugins.incoming_notification_vars.default_flash_icon =   q["hwindow"].config.vars["default_flash_on_icon"];
      hbl.plugins.incoming_notification_vars.flashicon = true;
      hbl.plugins.incoming_notification_vars.default_icon = hbl.plugins.incoming_notification_vars.remove_current_icon(1);
      
      if(! hbl.plugins.incoming_notification_vars.default_icon )
        hbl.plugins.incoming_notification_vars.default_icon =   q["hwindow"].config.vars["default_flash_off_icon"];
        
      hbl.util.debug("got here --------------------------------------");
    }
    
  };
  
  this.onReceiveMessage = function(arg) {
    if(arg["type"] == "remote_update") {
      // highlight it here
      arg["window"].highlight();
      hbl.plugins.incoming_notification_vars.flashme = true;
          
      // I can probably look in the buffer
      hbl.plugins.incoming_notification_vars.last_msg ="";
      if(  hbl.client.buffer &&  hbl.client.buffer[hbl.client.buffer.length -1] ){
        hbl.plugins.incoming_notification_vars.last_msg = hbl.client.buffer[hbl.client.buffer.length-1][1].substr(0,10);
        arg["window"].setHeader(".." + hbl.plugins.incoming_notification_vars.last_msg + "..");
      }
      
      // Set the title to note a new message
      if(hbl.plugins.incoming_notification_vars.last_msg != "" && !hbl.plugins.incoming_notification_vars.flashing)
        hbl.plugins.incoming_notification_vars.flash();
    }
  };
  
  this.onWindowFocus = function(arg) {
    if(hbl.oldtitle) { 
      document.title = hbl.oldtitle;
    }
    hbl.plugins.incoming_notification_vars.flashme = false;

    arg["window"].normal();
    
    // need to correct the status. (maybe)
  };
  
  hbl.plugins.incoming_notification_vars.flash = function() {
    hbl.plugins.incoming_notification_vars.flashing = true;
    
    if( document.title.indexOf("Hab.la Message Received")<0 && hbl.plugins.incoming_notification_vars.flashme) { 
      hbl.oldtitle    = document.title;
    
      if( ! hbl.oldtitle )
        hbl.oldtitle = "";
        
      document.title   = "(Hab.la Message Received: " + hbl.plugins.incoming_notification_vars.last_msg + ") " + hbl.oldtitle;
      
      if( hbl.plugins.incoming_notification_vars.flashicon ) {
        hbl.plugins.incoming_notification_vars.set_icon( hbl.plugins.incoming_notification_vars.default_flash_icon );
        
      }
      // I'll probably add the flashing favorite icon soon (and it will go here)
    } else {
      document.title   = hbl.oldtitle;
      
      if( hbl.plugins.incoming_notification_vars.flashicon ) {
        hbl.plugins.incoming_notification_vars.set_icon( hbl.plugins.incoming_notification_vars.default_icon );
      }
      
    }
    
    if(hbl.plugins.incoming_notification_vars.flashme) {
      setTimeout(hbl.plugins.incoming_notification_vars.flash, hbl.plugins.incoming_notification_vars.freq);
    }else {
      hbl.plugins.incoming_notification_vars.flashing = false;
    }
  };
  
  
  // modified from: http://www.ajaxify.com/run/favicon/favicon.js
  hbl.plugins.incoming_notification_vars.remove_current_icon = function (noremove) {
    if( ! document.getElementsByTagName("head") || document.getElementsByTagName("head").length == 0) return;
    var links = document.getElementsByTagName("head")[0].getElementsByTagName("link");
      for (var i=0; i<links.length; i++) {
        var link = links[i];
        if (link.type=="image/x-icon" && link.rel=="shortcut icon") {
          var href = link.href;
          if(! noremove) document.getElementsByTagName("head")[0].removeChild(link);
          return href; // Assuming only one match at most.
        }
      }
    return undefined;
  };
  
  // modified from: http://www.ajaxify.com/run/favicon/favicon.js
  hbl.plugins.incoming_notification_vars.set_icon = function (iconURL) {
    var link = document.createElement("link");
    link.type = "image/x-icon";
    link.rel = "shortcut icon";
    link.href = iconURL;
    hbl.plugins.incoming_notification_vars.remove_current_icon();
    if( ! document.getElementsByTagName("head") || document.getElementsByTagName("head").length == 0) return;
    document.getElementsByTagName("head")[0].appendChild(link);
  };


}; // end cool plugin

hbl.plugins.messages_received = 0;

hbl.plugins.expand_on_receive_message = function () {
  this.name = "expand_on_receive_message";
  
  this.load = function(q) {
    /* there are a few pieces to a hab.la plugin 
    
    1. check compatibility
    2. load additional event handlers
    3. celebrate?
    
    */
    hbl.util.debug("loading expand on receive message");
    /* check compatibility */
    if(q["conf"].vars["expandOnMessageReceived"] || q["conf"].vars["expandOnFirstMessageReceived"]) {
      // ok if urchinTracker is defined we are all set.
      
      q["hwindow"].eventmgr.register("receive_message", this.onReceiveMessage, -1 );
    
      /*
      q["client"]
      q["hwindow"]
      this.conf = q["conf"]
      */
    }
    
  };
  /* added a way to expand only on the first image */
  this.onReceiveMessage = function(arg) {
    hbl.plugins.messages_received += 1;
    if(arg["type"] != "start" && (!arg["window"].config.vars["expandOnFirstMessageReceived"] || hbl.plugins.messages_received ==1) ) {
      hbl.client.eventmgr.handle("window_expanded");
      arg["window"].expand();
      arg["window"].show();
    }
  }
  
};

/*
  Let's habla users define 3 divs, which can hide/show page elements based on 

*/

hbl.plugins.away_div_handler = function () {
  this.name = "away_div_handler";

  
  this.load = function(q) {
    /* there are a few pieces to a hab.la plugin 
    
    1. check compatibility
    2. load additional event handlers
    3. celebrate?
    */
    hbl.util.hide_div("habla_unavailable_div");
    hbl.util.hide_div("habla_available_div");
    if( ! hbl.util.show_div("habla_loading_div", q["hwindow"].config.vars["habla_special_div_show_type"] ) ) hbl.util.show_div("habla_unavailable_div", q["hwindow"].config.vars["habla_special_div_show_type"]);
    
  
    hbl.util.debug("loading hide/show a div based on operator status");
    /* check compatibility */
    if( document.getElementById("habla_available_div") != undefined || document.getElementById("habla_unavailable_div") != undefined) {       
      q["hwindow"].eventmgr.register("operator_status_change", this.onOperatorStatusChange );
    }
    
  };
  
  // Some event handlers
  this.onOperatorStatusChange = function(arg) {
    /* hide/show the image thingy */
    var available = arg["available"];
    var status    = arg["status"];
    var message   = arg["message"];

    /* ok here's what happens when op status changes */
    //hbl.habla.paint();
    if(available && (!status || status == "chat" || !arg["window"].config.vars["hide_when_away"]) ) {
      hbl.util.show_div("habla_available_div", arg["window"].config.vars["habla_special_div_show_type"] );
      hbl.util.hide_div("habla_unavailable_div");
      hbl.util.hide_div("habla_loading_div");   
    }else {
      hbl.util.hide_div("habla_available_div");
      hbl.util.show_div("habla_unavailable_div",arg["window"].config.vars["habla_special_div_show_type"] );
      hbl.util.hide_div("habla_loading_div");
    }
    
  };

}; // end this plugin

/*
 * Translate messages using google translate
 */

 /* A stub is a simple app that loads itsself :-)   */
hbl.plugins.googleanalytics = function () {
   this.name = "googleanalytics";
   
   this.load = function(q) {
     // A stub is a simple app that loads it's self
     if(window["urchinTracker"] != undefined && !q["conf"].vars["disableGoogleAnalytics"] ) {
     
       hbl.util.simple_load_js_async( q['conf'].vars['plugin_path'] + "google_analytics.js" );
     }
   }
};

/* I wish there was a better way */
hbl.plugins.google_translate = function () {
  this.name = "googletranslate";

  
   this.load = function(q) {
     hbl.util.debug("Loading google translate" + window["google"]  + q["conf"].vars["enableLanguageTranslation"]);
     if(window["google"] != undefined && window["google"]["load"] != undefined && q["conf"].vars["enableLanguageTranslation"] && q["conf"].vars["language"]) {
    
     //if( q["conf"].vars["enableLanguageTranslation"] && q["conf"].vars["language"]) {
       q["conf"].vars["poll"] = true;
       //hbl.util.simple_load_js_async("http://www.google.com/jsapi");
       
       hbl.util.debug("Loading google translate !!!! ");
       hbl.util.simple_load_js_async( q['conf'].vars['plugin_path'] + "google_translate.js" );
     }
    };
};
/*
Merge Extras back into core.

remove references to habla_window inside of the theme. -- if possible
remove references to hbl.eventmgr -- whenever possible.




----------------------------------------

   TODO 
      test for IE 

      HTML Code for use with theme.
    
DONE: 
    drop some functions into 'extras?'   
    [pallette name converter]
    
    (fix up library stuff -- Test with IE)
    
    style for Hab.la messages.
    SMART re-render--er


 To better support styling I want to do the following things

  config.style_classes[style_name]      = 'text of style (for class or substitution)
  config.style_id_classes['div_id']     = [styles_in_order,..]
  config.style_id['div_id']             = [text_of_style]

  to render:

  for each div_id in style classes
    for each [style_class]
      apply (class)

  for each id in style_id 
    apply normal style

  To implement:

  1.  rename all the div's in format   'habla_[element_name]_[element_type]'

  2.  add the above apply functions

  3. add config variables to disable applying the themes.

  3.5 - .. redo how render works, so it's div_id based.

  4. redo how the color/ setting etc.. works? -- maybe

  --- MAP from old palette to new palette
  --- + new palette vars
 */

hbl.themes.default_theme = function() {
  this.appended = true;
  this.last_msg = -1;
  
  this.visible       = false;
  this.highlighted   = false;
  this.state         = "compressed";
  
  // States:  compressed, closed, expanded, 
  // visible: true or false
  
  
  // adds the theme, handlers.. to the page
  this.build = function (config, client) {
    this.client = client;
    this.config = config;
    this.set_config();
    
    this.div    = undefined;
    this.divid  = config.vars["divid"];
    
    this.build_dom();
    this.set_dom_event_handlers();
    this.set_styles();
    this.set_default_text();
    this.register_handlers();
  };
  
  this.set_config = function(){
    var t = new hbl.hconfig();
    /* set the default variables for T -- mostly CSS */
    
    
    t.style_classes = {
      /* functionality specific classes */
      habla_window_div_position              : '',
      habla_window_div_position_inline       : '',
      habla_window_div_position_floating     : 'position: $vars["position"]; bottom: $vars["bottom_margin_px"]; right: $vars["right_margin_px"];',
      habla_window_div_position_floating_ie  : 'position: $vars["position_ie6"]; bottom: $vars["bottom_margin_px"]; right: $vars["right_margin_px"];',

      habla_window_div_base   : 'margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-family: verdana, arial, helvetica, sans-serif; text-align: left; vertical-align: baseline; line-height: 1;',
      /* general classes */
      habla_panel_border : 'background: $palette["main_bg"]; width: $vars["width_px"]; border: 1px solid black; font-size: 14px; font-family: "Lucida Grande", verdana, helvetica, arial, sans-serif',

      /* habla size/close Button */
      habla_button_a_highlighted : 'background-color: $palette["button_bg_highlight"]; color: $palette["button_fg_highlight"];',
      habla_button_a_hover       : 'background-color: $palette["button_bg_hover"]; color: $palette["button_fg_hover"];',
      habla_button_a_normal      : 'background-color: $palette["button_bg"]; color: $palette["button_fg"];',
      habla_button               : 'float: right; padding: 0px 6px 2px 6px; margin-left: 3px; font-weight: bold; text-decoration: none', 


      /* conversation related classes */
      habla_conversation_message_off : 'margin: 0; padding:3px 3px 3px 23px;',   /* for busy message etc.. */
      habla_conversation_message_on : 'margin: 0; padding:5px 5px 5px 5px;',   /* for busy message etc.. */

      habla_conversation_p_item  : 'color: $palette["main_fg"]; margin: 0; padding: 0; text-indent: -20px; background: transparent;', /* class for each paragraph */
      habla_conversation_person1 : 'color: $palette["local"] ; padding-right: 5px;',
      habla_conversation_person2 : 'color: $palette["remote"] ; padding-right: 5px;',
      
      habla_conversation_text_span : 'color: $palette["main_fg"]',
      
      habla_topbar_div_highlighted : 'background: $palette["title_bg_highlight"]; color: $palette["title_fg_highlight"]',
      habla_topbar_div_normal      : 'background: $palette["title_bg"]; color: $palette["title_fg"]',
      habla_topbar_clickable       : 'cursor: pointer; cursor: hand;',
      habla_oplink_a_hover         : 'color: $palette["title_fg_hover"]; text-decoration: underline;',
      habla_oplink_a_normal        : 'color: $palette["title_fg"]; text-decoration: none;',

      habla_wcsend_input_normal       : 'border: 2px solid $palette["control"];',
      habla_wcsend_input_highlighted  : 'border: 2px solid $palette["control"];'
    };
    
    /* default it to floating */
    t.style_classes['habla_window_div_position'] = t.style_classes['habla_window_div_position_floating'];
    
    /* maps styles to classes */
    t.style_classes_map = {
      habla_window_div     : ['habla_window_div_position', 'habla_window_div_base'],
      habla_closebutton_a  : ['habla_button', 'habla_button_a_normal'],
      habla_sizebutton_a   : ['habla_button', 'habla_button_a_normal'],
      habla_panel_div      : ['habla_panel_border'],
      habla_oplink_a       : ['habla_oplink_a_normal'],
      habla_wcsend_input   : ['habla_wcsend_input_normal'],
      habla_topbar_div     : ['habla_topbar_div_normal'] /* habla_topbar_clickable */
    };

    t.style_id = {
      habla_window_div       : 'z-index:3000',
      habla_panel_div        : '',
      habla_both_div         : '',
      habla_topbar_div       : 'padding: 5px;',
      habla_expanded_div     : '',
      habla_compressed_div   : '',
      habla_closed_div       : '',
      habla_oplink_a         : 'font-weight: normal;',
      habla_closebutton_a    : '',
      habla_sizebutton_a     : '',
      habla_middle_div       : '',
      habla_conversation_div : 'height: $vars["convo_height_px"]; overflow: auto; border-bottom: 1px dotted $palette["control"]; background: transparent; line-height: 1.5em; padding:3px 3px 3px 23px',
      habla_chatform_form    : 'margin: 0; padding: 0',
      habla_input_div        : 'padding: 3px; margin: 0; font-family: verdana, arial, helvetica; font-size: 12px; font-weight: normal;',   /* line-height: $vars["input_height_px"]; */
      habla_wcsend_input     : 'padding: 1px 3px 1px 3px; margin: 0; font-family: verdana, arial, helvetica; font-size: 12px; width: $vars["input_width_px"]; height: $vars["input_height_px"];  background: none; vertical-align:text-top; overflow-x: none; overflow-y: none; overflow:auto',
      habla_say_text_span    : 'color: $palette["main_fg"];',
    
      /* oh well.. */
      habla_link_div         : 'padding: 3px 0 5px 0; font-family: verdana, sans-serif; text-align: center; text-transform: uppercase; font-size: 9px; letter-spacing: 2px; font-weight: bold; color: #aaa;'
    };

    /* set variables for the close/size button's text*/
    t.vars = {
      habla_sizebutton_text_expanded : "",
      habla_sizebutton_text_compressed : "",
      habla_closebutton_text : "x"
    };



    /* do the positioning correctly */
    if(hbl.util.BrowserDetect.backwards_position) {
      t.style_classes['habla_window_div_position'] = t.style_classes['habla_window_div_position_floating_ie'];
    }
    /* this should only take effect if habla_window_div_position is not alread defined */


    // set all the vars on T, and then merge it.
    this.config.merge(t);
    // we take this.config, and than apply theme specific configuration variables to it?
    var t2 = new hbl.hconfig();
    t2.load_defaults();
    
    // now load all the other defaults
    this.config.merge( t2 );
    this.config.remap_palette(); /* just in case.. people have themed it */
    
    this.config.init_from_config();
    
    hbl.util.debug("--------------------------------");
    hbl.util.debug(this.config.vars);
    
    // ok we should be good to go now.
  };
  
  this.build_dom = function() {
    // set the parent div for everything else
    // in general I want to have ONE parent DIV
    // that everything sits in.. 
    // so if I want to HIDE and perserve state.. I just hide the parent.
    
    // see if it exists
    if(document.getElementById(this.divid)) {
      this.appended = false;
    }
	
    this.habla_window_div = hbl.util.find_or_create_div(this.divid, (this.config.vars["not_append"] == undefined ? hbl.util.get_body() : undefined) );

    // hide everything while we are figuring it out.
    hbl.util.hide_div(this.habla_window_div);
    
    this.habla_panel_div     = hbl.util.find_or_create_div("habla_panel_div", this.habla_window_div);
    this.habla_both_div     = hbl.util.find_or_create_div("habla_both_div", this.habla_panel_div);
    // shown for expanded and compressed
    
    /* ok it's hidden -- build it all up */
    this.habla_topbar_div = hbl.util.find_or_create_div("habla_topbar_div", this.habla_both_div);
    
    this.habla_expanded_div = hbl.util.find_or_create_div("habla_expanded_div", this.habla_panel_div);
    // shown when expanded
    
    this.habla_compressed_div = hbl.util.find_or_create_div("habla_compressed_div", this.habla_panel_div);    
    // shown when compressed
    
    this.habla_closed_div = hbl.util.find_or_create_div("habla_closed_div", this.habla_window_div);    
    /* shown only when item is closed. */
    
    
    /* Should really have an onmouseover
       and on mouse out for the topbar
    */
    
    this.hide();
    
    
    /* operator status link */
    this.habla_oplink_a = hbl.util.find_or_create_el("habla_oplink_a", "a");
    //this.habla_oplink_a.setAttribute("href","#");
   
    
    /* what do I do if there is no min or max button ? */
    if(this.config.vars["enable_buttons"]) {

      if( !this.config.vars["hide_min_max_buttons"] ) { // just hide the min_max buttons
        
  
        this.habla_closebutton_a = hbl.util.find_or_create_el("habla_closebutton_a", "a");
        //this.habla_closebutt_a.setAttribute("class","habla_button");
        //this.habla_closebutton_a.setAttribute("href","#");
        //obj.closebutt.onclick = obj.closeClicked;



        this.habla_topbar_div.appendChild(this.habla_closebutton_a);
    
        this.habla_sizebutton_a = hbl.util.find_or_create_el("habla_sizebutton_a", "a");
        //this.sizebutt.setAttribute("class","habla_button");
        //this.habla_sizebutton_div.setAttribute("href","#");  // I am not sure if I need this or not.. I should get rid of it.
        //obj.minbutt.onclick = obj.topBarClicked;

        this.habla_topbar_div.appendChild(this.habla_sizebutton_a);
      } // let people just hide the min/max buttons
      
      
      
    }
    this.habla_topbar_div.appendChild(this.habla_oplink_a);
    
    this.habla_middle_div = hbl.util.find_or_create_div("habla_middle_div", this.habla_expanded_div);
    //obj.middle.innerHTML = "";// just in case it got filled in
    //obj.away_message = hbl.util.find_or_create_div("habla_away_message", obj.middle);
    
    this.habla_conversation_div = hbl.util.find_or_create_div("habla_conversation_div", this.habla_middle_div);
    
    /** The chat form **/
    this.habla_chatform_form = hbl.util.find_or_create_el("habla_chatform_form", "form");
    this.habla_chatform_form.setAttribute("action","#"); /* not sure if I need this */
    this.habla_chatform_form.setAttribute("method","GET");
    this.habla_chatform_form.setAttribute("autocomplete","off");
    
    this.habla_input_div = hbl.util.find_or_create_el("habla_input_div", "div");
    
    this.habla_wcsend_input = hbl.util.find_or_create_el("habla_wcsend_input", "textarea"); /* I probably want to convert this over */
    //this.habla_wcsend_input.setAttribute("type", "text");

    /* may want to change this variable */ 
    this.habla_wcsend_input.setAttribute("size",this.config.vars["input_box_size"] );
    
    // add this function
    // I'll need to fool around with the event handlers to make it all make sense
    // probably I'll need to define them somewhere in the namespace.. or maybe.. 
    // they could be attached to something passed to this function?
    
    
    this.habla_say_text_span = hbl.util.find_or_create_el("habla_say_text_span", "span");

    
    this.habla_input_div.appendChild(this.habla_say_text_span);
    this.habla_input_div.appendChild(this.habla_wcsend_input);
    
    this.habla_chatform_form.appendChild(this.habla_input_div);
    this.habla_middle_div.appendChild(this.habla_chatform_form);

    /* finally the bottom */
    
    /* I need to move all this into the WINDOW */
    /* hmm.. I might keep this weird for now.. let's think about it */

    
  };
  
  // need to wrap some of these into 
  // generic event handlers
  
  this.set_dom_event_handlers = function() {
    
    // conversation
    this.habla_conversation_div.onclick    = function() { return habla_window.eventmgr.handle("habla_conversation_div_onclick");};
    
    
    //Chat form
    this.habla_chatform_form.onfocus    = function() { habla_window.eventmgr.handle("habla_chatform_form_onfocus");};
    
    this.habla_chatform_form.onsubmit   = function(e) { habla_window.eventmgr.handle("window_form_submit", {'event' : e}); return false;};
    
    // Submit Events
    this.habla_wcsend_input.onkeypress = function(e) { 
                                                        /* handle input processing on the form */
                                                        if(!e ) e = window.event;
                                                        habla_window.eventmgr.handle("window_submit", {'event' : e});
                                                        if( window.event )  {
                                                          e = window.event;
                                                          hbl.util.debug(e);
                                                          keynum = e.keyCode;
                                                        } else if( e.which ) {
                                                          keynum = e.which;
                                                        }
                                                        if(keynum == 13) return false;
                                                        return true;
                                                      };
    
    //function() { return hbl.hwindow.submit_on_enter(event) };
    // I can change this later.. if it turns out it matters
    this.habla_wcsend_input.onfocus        = function() { habla_window.eventmgr.handle("habla_wcsend_input_onfocus");};
    this.habla_wcsend_input.onclick        = function() { habla_window.eventmgr.handle("habla_wcsend_input_onclick");};
    this.habla_wcsend_input.onmouseover    = function() { habla_window.eventmgr.handle("habla_wcsend_input_onmouseover");};  
  
    /* I need to abstract all these little functions out */

    // how do I handle roll overs.. when theming?
    this.habla_oplink_a.onmouseover = function() { habla_window.eventmgr.handle("habla_oplink_a_onmouseover");};  
    this.habla_oplink_a.onmouseout  = function() { habla_window.eventmgr.handle("habla_oplink_a_onmouseout");};
    
    
    /* what do I do if there is no min or max button ? */
    if(this.config.vars["enable_buttons"]) {
      
      this.habla_topbar_div.onclick = function() { habla_window.eventmgr.handle("window_topbar_clicked"); return false;};
      this.habla_oplink_a.onclick = function() { habla_window.eventmgr.handle("window_oplink_clicked"); return false;};
      
      if( !this.config.vars["hide_min_max_buttons"] ) { // just hide the min_max buttons
        
        // hmm... 
        this.habla_closebutton_a.onmouseover = function() { habla_window.eventmgr.handle("habla_closebutton_a_onmouseover");};
        this.habla_closebutton_a.onmouseout = function() { habla_window.eventmgr.handle("habla_closebutton_a_onmouseout");};
        this.habla_closebutton_a.onclick    = function() { habla_window.eventmgr.handle("habla_closebutton_a_onclick"); return false;};
        
              
        this.habla_sizebutton_a.onmouseover = function() { habla_window.eventmgr.handle("habla_sizebutton_a_onmouseover");};
        this.habla_sizebutton_a.onmouseout = function() { habla_window.eventmgr.handle("habla_sizebutton_a_onmouseout");};
        this.habla_sizebutton_a.onclick    = function() { habla_window.eventmgr.handle("habla_sizebutton_a_onclick"); return false;};

      } // let people just hide the min/max buttons
      
      /* set the class here */
      this.config.style_classes_map['habla_topbar_div'] = ['habla_topbar_div_normal', 'habla_topbar_clickable'];
      
      
    }else {
      this.habla_oplink_a.onclick = function() { return false;};
    }

  };
 
  // Set the styles
  this.set_styles = function() {
    // I need a better style renderer.
    
    if(! this.config.vars["disableJSStyles"]) {
      this.config.render_all_styles(this);
    }

    /* I should probably just migrate this stuff over  -- this can just ensure that it's not initially hidden :-) */
    //this.config.render_js_style(this.habla_link_div, "habla_link_div");
    // people who are looking hard can probably find this... but most other people won't be able to.
    
    if( document.getElementById('hblink1') ) {
      this.config.render_js_style(document.getElementById('hblink1') , "habla_link_a");
      this.config.render_js_style(document.getElementById('hblink2') , "habla_link_a");
    }
    
  };
  
  // hbl.util.as_dom
  this.set_default_text = function() {
    // uses config to set the default text fields, before anything else is setup
    
    // might want to break this out
    this.habla_say_text_span.innerHTML = this.config.vars["say_text"];
    
    if(this.habla_closebutton_a) this.habla_closebutton_a.innerHTML = this.config.vars['habla_closebutton_text']; // hmm.. move this into a config var?
    if(this.habla_sizebutton_a)  this.habla_sizebutton_a.innerHTML = this.config.vars['habla_sizebutton_text_compressed'];   // to expand
    
    this.setHeader(this.config.vars["check_for_status"]);  
    this.setBody( this.config.vars["welcome_msg"] );  
  };
  
  
  this.register_handlers = function() {
    
    
    hbl.eventmgr.register("habla_conversation_div_onclick", 
      function(arg) {
            habla_window.eventmgr.handle("window_click");
      }
      ,0 );

    hbl.eventmgr.register("habla_chatform_form_onfocus", 
      function(arg) {
            habla_window.eventmgr.handle("window_focus");
      }
      ,0 );
  
    
    
    // Register handlers for all the events I need to have.
    hbl.eventmgr.register("habla_sizebutton_a_onmouseover", 
      function(arg) {
        arg["window"].config.render_class(arg["window"].theme.habla_sizebutton_a, "habla_button_a_hover");
      }
      ,0 );

    hbl.eventmgr.register("habla_sizebutton_a_onmouseout", 
       function(arg) {
         arg["window"].config.render_class(arg["window"].theme.habla_sizebutton_a, "habla_button_a_normal");
       }
       ,0 );

     hbl.eventmgr.register("habla_sizebutton_a_onclick", 
        function(arg) {
          hbl.eventmgr.handle("window_sizebutt_clicked");
          arg["window"].config.render_class(arg["window"].theme.habla_sizebutton_a, "habla_button_a_normal");
        }
        ,0 );

    hbl.eventmgr.register("habla_closebutton_a_onmouseover", 
      function(arg) {
        arg["window"].config.render_class(arg["window"].theme.habla_closebutton_a, "habla_button_a_hover");
      }
      ,0 );
      
    hbl.eventmgr.register("habla_closebutton_a_onmouseout", 
      function(arg) {
        arg["window"].config.render_class(arg["window"].theme.habla_closebutton_a, "habla_button_a_normal");
      }
      ,0 );

    hbl.eventmgr.register("habla_closebutton_a_onclick", 
       function(arg) {
         arg["window"].config.render_class(arg["window"].theme.habla_closebutton_a, "habla_button_a_normal");
       }
       ,0 );

     /* focus events */
     hbl.eventmgr.register("habla_wcsend_input_onfocus", 
        function(arg) {
          habla_window.eventmgr.handle("window_focus");
        }
        ,0 );

      hbl.eventmgr.register("habla_wcsend_input_onclick", 
         function(arg) {
           habla_window.eventmgr.handle("window_focus");
         }
         ,0 );

       hbl.eventmgr.register("habla_wcsend_input_onclick", 
          function(arg) {
            habla_window.eventmgr.handle("window_focus");
          }
          ,0 );         

      hbl.eventmgr.register("habla_wcsend_input_onmouseover", 
         function(arg) {
           habla_window.eventmgr.handle("window_focus");
         }
         ,0 );         

     hbl.eventmgr.register("habla_oplink_a_onmouseover", 
       function(arg) {
         arg["window"].config.render_class(arg["window"].theme.habla_oplink_a, "habla_oplink_a_hover"); 
       }
       ,0 );   

     hbl.eventmgr.register("habla_oplink_a_onmouseout", 
        function(arg) {
          arg["window"].config.render_class(arg["window"].theme.habla_oplink_a, "habla_oplink_a_normal");
        }
        ,0 );
    
  };
  
  /* standard methods for all themes  ---------------------------------------------------------------------- */
  
  // removes the theme from the page.  (if it was appended)
  this.remove = function() {
    if( this.appended ) {
      hbl.util.remove_element(this.divid);
    }
  };
  
  this.setBody    = function(txt) {
    // Put text into the body of the message
    this.habla_conversation_div.innerHTML = "";
    
    this.config.render_class(this.habla_conversation_div, 'habla_conversation_message_off');
    
    if(txt != "") { 
      var txt = hbl.util.as_dom(txt);
      this.config.render_class(this.habla_conversation_div, 'habla_conversation_message_on');      
      this.habla_conversation_div.appendChild(txt);
      
    }
    
    this.habla_conversation_div.scrollTop = this.habla_conversation_div.scrollHeight; 
  };
  
  this.getBody = function() {
    return this.habla_conversation_div.innerHTML;
  };
  
  /*
   * This function appends content to the chat window 'nicely'
   * running the display pipeline on it,
   * and formatting it
   */
  this.appendNiceMessage = function( sender, txt, clear_body) {
    // formatting pipeline
    if(txt == undefined || sender == undefined) return;
    
     var txt = this.display_pipeline.run(txt);
     if(txt) {
       var cls = "";
       
       
       if(sender == this.config.vars['myname']) {
         // one sender
         cls = "habla_conversation_person1";
       }else {
         // the other
         cls = "habla_conversation_person2";
       }

       // start building element
       var message = document.createElement("p");
       message.setAttribute("class", "habla_conversation_p_item");
       // style it
       this.config.render_class(message, "habla_conversation_p_item");  /* render a class */
       
       var span  = document.createElement("span");
       span.setAttribute("class", cls);
       
       if(sender == this.config.vars['myname']) {
         span.innerHTML = this.config.vars['local_user_display_name'];
         this.config.render_class(span, "habla_conversation_person1");   /* render a class */
       }else{ 
         span.innerHTML = (this.config.vars['local_name_override'] ? this.config.vars['local_name_override'] : sender ) + " : ";
         this.config.render_class(span, "habla_conversation_person2");   /* render a class */
       }
       
       var span2  = document.createElement("span");
       span2.setAttribute("class", "habla_conversation_text_span");
       span2.innerHTML = txt;
              
       message.appendChild(span);
       message.appendChild(span2);
       
       this.appendMessage( message, clear_body);
    }   
       
       

  };
  
  // takes a message and appends it to the body
  this.appendMessage = function( txt, clear_body ) {
    // appends a message
    // might be useful to style it.. -- do I do that here?
    
    if(this.last_msg == -1 && clear_body) {
      this.setBody(""); // clear body on first message
                        // actually it would be good to know if
                        // the operator is online or online at this moment in time.
                        // and only clear, if the operator is online.
    }
    
    // maybe..
    var txt = hbl.util.as_dom(txt);
    this.last_msg += 1;
    txt.setAttribute("id", "habla_msg_" + this.last_msg);
    
    // in theory I could do more here..
    // but we can assume it's already formatted in some way.
    // or maybe toss a pipeline in here to format it.
    // as part of the theme.
    this.habla_conversation_div.appendChild(txt);
    this.habla_conversation_div.scrollTop = this.habla_conversation_div.scrollHeight;
    
  };
  
  // set Header text
  this.setHeader = function (txt ) {
      // may want to convert this to a SPAN? .. nah
      this.habla_oplink_a.innerHTML = txt;
  };
  
  this.getHeader = function ( ) {
      // may want to convert this to 
      return this.habla_oplink_a.innerHTML;
  };
  
  this.hide = function () {
    /* execute the code to hide the theme */
    // hide all the key parts
    this.visible = false;
    
    if(this.habla_conversation_div) this.habla_conversation_div.style.overflow = "hidden";
    
    hbl.util.hide_div(this.habla_window_div);
    hbl.util.hide_div(this.habla_both_div);
    hbl.util.hide_div(this.habla_closed_div);
    hbl.util.hide_div(this.habla_expanded_div);
    hbl.util.hide_div(this.habla_compressed_div);      
  };
  
  this.expand = function() {
    /* executes the code to expand the theme */
    this.state = "expanded";
    
    if(this.habla_sizebutton_a)  this.habla_sizebutton_a.innerHTML = this.config.vars['habla_sizebutton_text_expanded'];   // to expand
    
    this.show();
    
  };
  
  this.compress = function() {
    /* executes the code to compress the theme */
    this.state = "compressed";
    
    if(this.habla_sizebutton_a)  this.habla_sizebutton_a.innerHTML = this.config.vars['habla_sizebutton_text_compressed'];   // to expand
    
    // might set the text fields for compressed here?
    this.show(); 
  };
  
  /*
    Show essentially, RENDERs state to the right
   */
  this.show = function() {
    
    this.visible = true;
    /* executes the code to show the theme */
    hbl.util.show_div(this.habla_window_div);
    
    if(this.state == "closed") {
      // Hide the un-needed DIVS
      hbl.util.hide_div(this.habla_expanded_div);
      hbl.util.hide_div(this.habla_panel_div);
      hbl.util.hide_div(this.habla_compressed_div);

      
      this.habla_conversation_div.style.overflow = "hidden";
      // show the DIV we need
      hbl.util.show_div(this.habla_closed_div);
    }
    else if(this.state == "expanded") {
      hbl.util.hide_div(this.habla_compressed_div);
      hbl.util.hide_div(this.habla_closed_div);
      
      hbl.util.show_div(this.habla_panel_div);
      hbl.util.show_div(this.habla_expanded_div);
      hbl.util.show_div(this.habla_both_div);
      this.habla_conversation_div.style.overflow = "auto";
      
      // Other fancy stuff for expanded
      this.habla_conversation_div.scrollTop = this.habla_conversation_div.scrollHeight;
      this.habla_wcsend_input.focus();
      
      if(this.habla_sizebutton_a)  this.habla_sizebutton_a.innerHTML = this.config.vars['habla_sizebutton_text_expanded'];   // to expand [I wish..]
      
      // Stupid Safari 3 fix.. probably won't hurt, 10 ms is not very long
      setTimeout(function() { habla_window.theme.getMessageInputField().focus(); }, 10);
      
    }
    else if(this.state == "compressed") {
      hbl.util.hide_div(this.habla_expanded_div);
      hbl.util.hide_div(this.habla_closed_div);

      hbl.util.show_div(this.habla_panel_div);      
      hbl.util.show_div(this.habla_both_div);
      hbl.util.show_div(this.habla_compressed_div);
      this.habla_conversation_div.style.overflow = "hidden";
      
      if(this.habla_sizebutton_div)  this.habla_sizebutton_div.innerHTML = this.config.vars['habla_sizebutton_text_compressed'];   // to expand maybe a better way to customize?
    }
    
  };
  
  this.close = function() {
    /* executes the code to show the theme */
    this.state = "closed";
    this.show();
  };

  // a funky, yet useful function
  this.setWidth = function(width) {
    // filter for only the #

    this.div.style.width = width + "px";
    if( hbl.util.BrowserDetect.backwards_dimension ) {
      hbl.util.debug("212/24");
      this.habla_wcsend_input.style.width     = (width - 40) + "px"; 
      //this.wcsend.height     = (width - 38) + "px";   
    } else {
      hbl.util.debug("202/18");   
      this.habla_wcsend_input.style.width     = (width - this.config.vars["input_width_offset"]) + "px"; 
    //  this.wcsend.height     = (width - 38) + "px";
    }
  
  };
  
  this.setInputHeight = function(height) {
    // filter for only the #
    //alert("setting input height" + height);
    
    if( hbl.util.BrowserDetect.backwards_dimension ) {
      hbl.util.debug("212/24");
      this.habla_wcsend_input.style.height     = (height > 24? height : 24 ) + "px"; 
      //this.wcsend.height     = (width - 38) + "px";   
    } else {
      hbl.util.debug("202/18");   
      this.habla_wcsend_input.style.height     = (height > 18? height : 18 ) + "px"; 
    //  this.wcsend.height     = (width - 38) + "px";
    }
    //alert(this.habla_wcsend_input.style.height);
  
  };
  
  
  
  
  this.getX = function(){
    return this.habla_window_div.style.left.match(/\d\d*/);
  };

  this.getY = function(){
    return this.habla_window_div.style.top.match(/\d\d*/);
  };

  
  this.setXY = function(x,y){
    this.habla_window_div.style.top = y;
    this.habla_window_div.style.left = x;
    /* the window level setXY, should probably have some sort of .. event */
  };
  
  this.setHeight = function(height) {
    // I can make this a lot smarter 
    this.habla_conversation_div.style.height = height + "px";
  };

  this.highlight = function() {
    if (this.highlighted) return;

    /* I need to figure out how this works.. it seems like it is applying a class */

    /* ok, highlight the window now*/    
    this.config.render_class(this.habla_closebutton_a, "habla_button_a_highlighted");
    this.config.render_class(this.habla_sizebutton_a, "habla_button_a_highlighted");
    
    this.config.render_class(this.habla_topbar_div, "habla_topbar_div_highlighted");
    this.config.render_class(this.habla_wcsend_input, "habla_wcsend_input_highlighted");
    this.highlighted = true;

  };
  this.normal   = function() {
    /* turn the window normal */
    if(!this.highlighted) return; 

    this.config.render_class(this.habla_closebutton_a, "habla_button_a_normal");
    this.config.render_class(this.habla_sizebutton_a, "habla_button_a_normal");

    /* also applying a class */
    this.config.render_class(this.habla_topbar_div, "habla_button_a_normal");
    this.config.render_class(this.habla_topbar_div, "habla_topbar_div_normal");
    this.config.render_class(this.habla_wcsend_input, "habla_wcsend_input_normal");
    this.highlighted = false;
  };

  this.getMessageInputField = function() {
    return this.habla_wcsend_input;
  };

  this.getMessageInputFieldValue = function() {
    return this.habla_wcsend_input.value;
  };
  this.setMessageInputFieldValue = function(val) {
    this.habla_wcsend_input.value = val;
  };
  
  
  
  // Display pipeline will be theme related?
  this.display_pipeline = new hbl.util.pipeline(this);
  this.display_pipeline.add(hbl.pipelines.wrap_text,999);
  this.display_pipeline.add(hbl.pipelines.emoticons,1000);
  
};


hbl.eventmgr.register("habla_preload_done", 
   function(arg) {
     if(! hbl.config.vars["theme_path"] && ! hbl.config.vars["theme_obj"]) {
       hbl.config.vars["theme_obj"] = new hbl.themes.default_theme();
     }
   }
   ,0 );

//for post definition
if(hbl.config) {
  if(! arg['window'].config.vars["theme_path"] && ! hbl.config.vars["custom_theme"] ) {
    hbl.eventmgr.handle("theme_loaded",  {'obj' : new hbl.themes.default_theme() } );
  }
}



/* define wc_init and wc_config */
function wc_config() {
  return new hbl.hconfig();
};


hblHasinit = undefined;
function wc_init(id, config ) {
  if(hblHasinit) return;
  hblHasinit = 1;
  //First thing we'll do is make sure we are using a supported browser
  hbl.util.BrowserDetect.init();
  if(! (hbl.util.BrowserDetect.supported) && hblHideUnsupported){
    hbl.util.debug("The Browser you are using is not supported by Hab.la - " + hbl.util.BrowserDetect.browser + hbl.util.BrowserDetect.supported);
    return;
  }
  hbl.siteid = id;
  
  // get cookies
  hbl.wcsid = hbl.util.get_cookie("wcsid") || "";
  hbl.hblid  = hbl.util.get_cookie("hblid") || "";
  
  hbl.config = config;
  if(hbl.config == undefined )
    hbl.config = new hbl.hconfig();
  
  if(!hbl.config.merge) {
    hbl.config = new hbl.hconfig();
    hbl.config.merge(config, true);
  }
  
  // I probably want to change how plugins work
  
  // Add plugins:
  if(!hbl.config.plugins) {
    hbl.config.plugins = new hbl.util.pluginlist();
  }
  
  hbl.config.plugins.add(new hbl.plugins.incoming_notification() );
  hbl.config.plugins.add(new hbl.plugins.googleanalytics() ); 
  hbl.config.plugins.add(new hbl.plugins.away_div_handler() );  
  hbl.config.plugins.add(new hbl.plugins.expand_on_receive_message() );
  hbl.config.plugins.add(new hbl.plugins.google_translate() );
      


  hbl.started = 0;
  
  hbl.eventmgr.handle("habla_preload_done");
    
  hbl.prev_onload = window.onload;
  window.onload = function() { habla_start_func(hbl.prev_onload); };
  
  // My own special hack here for setTimeout
  window.setTimeout(habla_start_func, 2500);

}

/**
  I am not 100% convinced how we want to start hab.la, extracting it to this function gives me some leeway.
  
**/


function habla_start_func(f_after) {
    if(hbl.started) return;
    hbl.started=1;

    if( f_after && typeof( f_after) == "function") {
       try {
        f_after();
          } catch(e) {}   
    }
    
    // Maybe you create the window here
    hbl.util.debug("onload");
    hbl.util.debug(hbl.siteid);
    hbl.util.debug(hbl.wcsid);

    habla_window = new hbl._hwindow(hbl.client, hbl.config, hbl.eventmgr);
    hbl.eventmgr.setWindow(habla_window);
    habla_window.register_handlers(); // register the event handlers
    
    
    // Hmm.. I need a good method of letting this work.
    // the problem is, without a theme... This won't get shown
    // and a theme only appears after a SITE-ID.. 
    // HMMM...
    
    // I need a REALLY SIMPLE DEBUGGING THEME
    // it doesn't even NEED EVENTS -- well it might need to expand.. and contract.
    // hmm..
    
    // For now I'll just preload the entire theme.
    
    if(hbl.config.vars["local_mode"]) {
        hbl.client.start(hbl.wcsid, hbl.hblid, hbl.siteid,  hbl.eventmgr, hbl.config);
        
        //hbl.client.eventmgr = new hbl.util.eventmanager();

        habla_window.setTheme(hbl.config.vars["theme_obj"]);

        habla_window.config.vars["welcome_msg"] = "Hab.la is in testing mode. set <em>config.vars['local_mode'] = false;</em> to go back to normal.";
        habla_window.config.vars["in_chat_text"]       = "Hab.la Local Mode";
        habla_window.config.vars["before_chat_text"]   = "Hab.la Local Mode";        
        habla_window.config.vars["not_available_text"] = "Hab.la Local Mode";
        habla_window.setHeader( habla_window.config.vars["not_available_text"]);
        habla_window.show(1);
      return;
    }
    /* Check to make sure the SITEID is correct */
    if(! hbl.siteid.match(/^\d\d*-\d\d*-\d\d*-\d\d*$/) && hbl.config.vars["theme_obj"] ) {
      /* The SITE ID is INVALID */
      // hmm...
      
      /* I could probably just (re)register the THEME loaded callback */
      /* and do this here as well... hmm... -- to still dynamically load the theme*/
      
      habla_error({'etype' : "invalid_site_id" });
              
      return;
    }
    /* register our error handler for begin */
    hbl.eventmgr.register("habla_error", habla_error, 10);
    
    hbl.client.start(hbl.wcsid, hbl.hblid, hbl.siteid,  hbl.eventmgr, hbl.config);
    
    // Add started event
    hbl.eventmgr.handle("habla_started");
  
}
/* right now we only have one type of ERROR -- this should probably be dropped into a function. */

function habla_error(arg) {
  if(arg['etype'] == "invalid_site_id") {
    hbl.client.eventmgr = new hbl.util.eventmanager();
  
    habla_window.setTheme(hbl.config.vars["theme_obj"]);
  
    habla_window.config.vars["offline_message"] = "<em>" + hbl.siteid + "</em> is not a valid <a href='http://www.hab.la'>Hab.la</a> site ID.<p> Your Hab.la Site ID is in the format '###-##-###-##' " + 
                                                  "and can be found on your <a href='http://hab.la/account' target='_blank'>My Hab.la Page</a>.</p>";
  
    habla_window.config.vars["not_available_text"] = "Hab.la: Site ID Error";
    habla_window.setHeader( habla_window.config.vars["not_available_text"]);
    habla_window.show(1);
    habla_window.theme.habla_conversation_div.scrollTop = 0;
  }else if(arg['etype'] == "operator_is_busy"){
    /* this should probably be handled somewhere else .. but eh.. */
    habla_window.setWindowText();
  }
  
}

function s5_open_habla() {
if (document.getElementById("habla_window_div")) {
document.getElementById("habla_window_div").style.display = "block";
document.getElementById("habla_conversation_div").innerHTML = "";
}
}
