//CVS:     $Id: ibox.js,v 1.7 2008/10/03 21:33:56 cvsdevel Exp $
//Title:   ibox.js
//Version: 1.03
//Author:  iBegin.com

/**
 * Creates and manages AJAX-enabled popup windows.
 * <p>
 * Original license information (v2.17c):
 * <pre>
 *   For more info & download: http://labs.ibegin.com/ibox/
 *   Created as a part of the iBegin iBegin Labs Project -
 *   http://labs.ibegin.com/
 *   For licensing please see readme.html (MIT Open Source License)
 * </pre>
 *
 * <p>
 * <b>Changelog:</b><pre>
 *  1.01  REVIE 2006/08/28  fixed non-IE spacing issues, added ability to move
 *                          window, changed "close" to image, and now hides
 *                          drop-down/iframe elements to fix IE6+ issues.
 *  1.02  REVIE 2007/03/15  limits title in window.
 *  1.03  REVIE 2008/09/23  upgraded to iBox v2.17c.
 * </pre>
 *
 * @author  iBegin.com
 * @version 1.03
 */

var iBox = function()
{
  var _pub = {
    // label for the close link
    close_label: 'Close',

    // padding around the box
    padding: 100,
    
    // show iframed content in the parent window
    // this *does not* work with #containers
    inherit_frames: false,

    // how fast to fade in the overlay/ibox (this is each step in ms)
    fade_in_speed: 0,

    // our attribute identifier for our iBox elements
    attribute_name: 'rel',
    
    // tags to hide when we show our box
    tags_to_hide: ['select', 'embed', 'object'],

    // default width of the box (when displaying html only)
    // height is calculated automatically
    default_width: 630,

    // browser checks
    is_opera: navigator.userAgent.indexOf('Opera/9') != -1,
    is_ie: navigator.userAgent.indexOf("MSIE ") != -1,
    is_ie6: false /*@cc_on || @_jscript_version < 5.7 @*/,
    is_firefox: navigator.appName == "Netscape" && navigator.userAgent.indexOf("Gecko") != -1 && navigator.userAgent.indexOf("Netscape") == -1,
    is_mac: navigator.userAgent.indexOf('Macintosh') != -1,
    is_moz: navigator.appName == "Netscape" && navigator.userAgent.indexOf(" Gecko/") != -1 && navigator.userAgent.indexOf("Firefox") == -1,

    base_url: '',
    
    /**
     * Updates the base_url variable.
     * @param {String} path Relative or absolute path to this file.
     */
    setPath: function(path)
    {
      _pub.base_url = path;
    },
    
    /**
     * Binds arguments to a callback function
     */
    bind: function(fn)
    {
        var args = [];
        for (var n=1; n<arguments.length; n++) args.push(arguments[n]);
        return function(e) { return fn.apply(this, [e].concat(args)); };
    },

    /**
     * Sets the content of the ibox
     * @param {String} content HTML content
     * @param {Object} params
     */
    html: function(content, params)
    {
      if (content === undefined) return els.content;
      if (cancelled) return;
      _pub.clear();
      els.wrapper.style.display = "block";
      els.wrapper.style.visibility = "hidden";
      els.content.style.height = 'auto';

      if (typeof(content) == 'string') {
        // if a full HTML page, only want the body content, since some
        // browsers get confused when the same CSS is loaded twice
        var i = content.toLowerCase().indexOf('<body');
        if (i != -1) i = content.indexOf('>', i);
        if (i != -1) {
          content = content.substr(i + 1, content.length - i - 1);
          i = content.toLowerCase().indexOf('</body');
          if (i != -1) content = content.substr(0, i);
        }

        els.content.innerHTML = content;
      } else {
        els.content.appendChild(content);
      }

      var elemSize = _pub.getElementSize(els.content);
      var pageSize = _pub.getPageSize();

      if (params.can_resize === undefined) params.can_resize = true;
      if (params.fade_in === undefined) params.fade_in = true;

      if (params.width) var width = parseInt(params.width);
      else var width = _pub.default_width;

      if (params.height) var height = parseInt(params.height);
      else var height = elemSize.height;

      els.wrapper.style.width = width + 'px';
      els.wrapper.style.height = height + 'px';

      // if we dont do this twice we get a bug on the first display
      if (!params.height)
      {
        var elemSize = _pub.getElementSize(els.content);
        var height = elemSize.height;
      }
      if (params.can_resize) _pub.resizeObjectToScreen(els.content, width, height, params.constrain);
      else
      {
        els.content.style.width = width + 'px';
        els.content.style.height = height + 'px';
      }

      // now we set the wrapper
      var elemSize = _pub.getElementSize(els.content);
      els.wrapper.style.width = elemSize.width + 'px';
      els.wrapper.style.height = elemSize.height + 'px';

      _pub.reposition();
      
      els.wrapper.style.visibility = "visible";
      _pub.fadeIn(els.wrapper, 10, params.fade_in ? _pub.fade_in_speed : 0);
    },
    
    /**
     * Empties the content of the iBox (also hides the loading indicator)
     */
    clear: function()
    {
      els.loading.style.display = "none";
      while (els.content.firstChild) els.content.removeChild(els.content.firstChild);
    },
    
    /**
     * Loads text into the ibox
     * @param {String} url
     * @param {String} title
     * @param {Object} params
     */
    show: function(text, title, params)
    {
      _pub.hide();
      showInit(title, params, function(){
        _pub.html(text, params);
      });
    },
    /**
     * Loads a url into the ibox
     * @param {String} url
     * @param {String} title
     * @param {Object} params
     */
    showURL: function(url, title, params)
    {
      showInit(title, params, function(){
        cancelled = false;
        for (var i=0; i<_pub.plugins.list.length; i++)
        {
          var plugin = _pub.plugins.list[i];
          if (plugin.match(url) && plugin.render(url, params))
          {
            active_plugin = plugin;
            break;
          }
        }
      });
    },

    /**
     * Hides the iBox
     */
    hide: function()
    {
      if (active_plugin)
      {
        // call the plugins unload method
        if (active_plugin.unload) active_plugin.unload();
        active_plugin = null;
      }
      //window.onscroll = null;
      _pub.clear();
      // restore elements that were hidden
      for (var i=0; i<_pub.tags_to_hide.length; i++) showTags(_pub.tags_to_hide[i]);

      els.loading.style.display = 'none';
      els.overlay.style.display = 'none';
      els.wrapper.style.display = 'none';
      _pub.fireEvent('hide');
      _pub.removeEvent(document, 'mousedown', _pub.check);
    },

    /**
     * Checks to see if the iBox can be closed (such as for mouse clicks
     * outside the box).
     */
    check: function(e)
    {
      e || (e = window.event);
      var cont = (e.target ? e.target : e.srcElement ? e.srcElement : null);
      if (cont) {
         if (cont.nodeType == 3) {
            cont = cont.parentNode;
         }
      }

      if (cont && cont.id &&
          (cont.id.toLowerCase() == 'ibox_wrapper' ||
           cont.id.toLowerCase() == 'ibox_footer' ||
           cont.id.toLowerCase() == 'ibox_footer_wrapper')) {
         // dragging window
         _pub.dragStart(e);
      } else if (cont) {
         while (cont) {
            if (cont.id && cont.id.length > 0 &&
                cont.id.toLowerCase() == 'ibox_wrapper') {
               // if here, click was within popup; do nothing
               return true;
            } else if (cont.tagName && cont.tagName.toLowerCase() == 'body') {
               // if here, out of bounds.  close window.
               _pub.hide();
               return _pub.stopEvent(e);
            } else {
               cont = cont.parentNode;
            }
         }
      }
    },

    /**
     * Resizes an object to fit on screen
     * @param {Object} obj
     * @param {Integer} width
     * @param {Integer} height
     * @param {Boolean} constrain
     */
    resizeObjectToScreen: function(obj, width, height, constrain)
    {

      var pagesize = _pub.getPageSize();

      var x = pagesize.width - _pub.padding;
      var y = pagesize.height - _pub.padding;
      
      if (!height) var height = obj.height;
      if (!width) var width = obj.width;
      if (width > x)
      {
        if (constrain) height = height * (x/width);
        width = x;
      }
      if (height > y)
      {
        if (constrain) width = width * (y/height);
        height = y;
      }
      obj.style.width = width + 'px';
      obj.style.height = height + 'px';
    },

    /**
     * Repositions the iBox wrapper (from events)
     */
    reposition: function(e)
    {
      // verify height doesnt overreach browser's viewpane
      _pub.dragging = false;
      _pub.center(els.loading);
      _pub.center(els.wrapper);
      var pageSize = _pub.getPageSize();
      var scrollPos = _pub.getScrollPos();

      if (els.overlay) {
        if (_pub.is_ie6) els.overlay.style.width = document.documentElement.clientWidth + 'px';
        var height = Math.max(document.documentElement.clientHeight, document.body.clientHeight);
        els.overlay.style.height = height + 'px';
      }
    },

    /**
     * Starts dragging the element.
     */
    dragStart: function(e)
    {
       if (_pub.dragging) {
          return true;
       }

       e || (e = window.event);
       var scrollPos = _pub.getScrollPos();
       var x = (e.clientX ? e.clientX : e.pageX) + scrollPos.scrollX;
       var y = (e.clientY ? e.clientY : e.pageY) + scrollPos.scrollY;

       _pub.dragging = true;
       _pub.dragPos = new Array();
       _pub.dragPos.x = x;
       _pub.dragPos.y = y;
       _pub.dragPos.startX = parseInt(els.wrapper.style.left);
       _pub.dragPos.startY = parseInt(els.wrapper.style.top);

       _pub.stopEvent(e); // prevent text selection
       _pub.addEvent(document, 'mousemove', _pub.dragIt);
       _pub.addEvent(document, 'mouseover', _pub.stopEvent);
       _pub.addEvent(document, 'mouseup', _pub.dragEnd);
    },

    /**
     * Drags the element to the new cursor location.
     */
    dragIt: function(e)
    {
       e || (e = window.event);
       var scrollPos = _pub.getScrollPos();
       var x = (e.clientX ? e.clientX : e.pageX) + scrollPos.scrollX;
       var y = (e.clientY ? e.clientY : e.pageY) + scrollPos.scrollY;

       els.wrapper.style.left =
         ((x - _pub.dragPos.x) + _pub.dragPos.startX) + 'px';
       els.wrapper.style.top =
         ((y - _pub.dragPos.y) + _pub.dragPos.startY) + 'px';
    },

    /**
     * Stops dragging the element.
     */
    dragEnd: function(e)
    {
       _pub.dragging = false;
       _pub.removeEvent(document, 'mousemove', _pub.dragIt);
       _pub.removeEvent(document, 'mouseover', _pub.stopEvent);
       _pub.removeEvent(document, 'mouseup', _pub.dragEnd);
    },

    /**
     * Centers an object
     * @param {Object} obj
     */
    center: function(obj)
    {
      var pageSize = _pub.getPageSize();
      var scrollPos = _pub.getScrollPos();
      var emSize = _pub.getElementSize(obj);
      var x = Math.round((pageSize.width - emSize.width) / 2 + scrollPos.scrollX);
      var y = Math.round((pageSize.height - emSize.height) / 2 + scrollPos.scrollY);
      if (obj) {
        obj.style.left = x + 'px';
        obj.style.top = y + 'px';
      }
    },
    
    getStyle: function(obj, styleProp)
    {
      if (obj.currentStyle)
        return obj.currentStyle[styleProp];
      else if (window.getComputedStyle)
        return document.defaultView.getComputedStyle(obj,null).getPropertyValue(styleProp);
    },

    /**
     * Gets the scroll positions
     */
    getScrollPos: function()
    {
      var docElem = document.documentElement;
      return {
        scrollX: document.body.scrollLeft || window.pageXOffset || (docElem && docElem.scrollLeft),
        scrollY: document.body.scrollTop || window.pageYOffset || (docElem && docElem.scrollTop)
      };
    },

    /**
     * Gets the page constraints
     */
    getPageSize: function()
    {
      return {
        width: window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || document.body.clientWidth,
        height: window.innerHeight || (document.documentElement && document.documentElement.clientHeight) || document.body.clientHeight
      };
    },

    /**
     * Gets an objects offsets
     * @param {Object} obj
     */
    getElementSize: function(obj)
    {
      return {
        width: (obj ? obj.offsetWidth || obj.style.pixelWidth : 0),
        height: (obj ? obj.offsetHeight || obj.style.pixelHeight : 0)
      };
    },

    fadeIn: function(obj, level, speed, callback)
    {
      if (level === undefined) level = 100;
      if (speed === undefined) speed = 70;
      if (!speed)
      {
        _pub.setOpacity(null, obj, level*10);
        if (callback) callback();
        return;
      }
    
      _pub.setOpacity(null, obj, 0);
      for (var i=0; i<=level; i++)
      {
        setTimeout(_pub.bind(_pub.setOpacity, obj, i*10), speed*i);
      }
      if (callback) setTimeout(callback, speed*(level+1));
    },

    /**
     * Sets the opacity of an element
     * @param {Object} obj
     * @param {Integer} value
     */
    setOpacity: function(e, obj, value)
    {
      //obj.style.opacity = value/100;
      //obj.style.filter = 'alpha(opacity=' + value + ')';
    },
    
    /**
     * Creates a new XMLHttpRequest object based on browser
     */
    createXMLHttpRequest: function()
    {
      var http;
      if (window.XMLHttpRequest)
      { // Mozilla, Safari,...
        http = new XMLHttpRequest();
        if (http.overrideMimeType)
        {
          // set type accordingly to anticipated content type
          http.overrideMimeType('text/html');
        }
      }
      else if (window.ActiveXObject)
      { // IE
        try {
          http = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
          try {
            http = new ActiveXObject("Microsoft.XMLHTTP");
          } catch (e) {}
        }
      }
      if (!http)
      {
        //alert('Cannot create XMLHTTP instance');
        return false;
      }
      return http;
    },
    
    addEvent: function(obj, evType, fn)
    {
      if (obj.addEventListener)
      {
        obj.addEventListener(evType, fn, false);
        return true;
      }
      else if (obj.attachEvent)
      {
        var r = obj.attachEvent("on"+evType, fn);
        return r;
      }
      else
      {
        return false;
      }
    },
    
    addEventListener: function(name, callback)
    {
      if (!events[name]) events[name] = new Array();
      events[name].push(callback);
    },
    
    fireEvent: function(name)
    {
        if (events[name] && events[name].length)
        {
          for (var i=0; i<events[name].length; i++)
          {
            var args = [];
            for (var n=1; n<arguments.length; n++) args.push(arguments[n]);
            // Events returning false stop propagation
            if (events[name][i](args) === false) break;
          }
        }
    },

    /**
     * Removes the scheduled event.
     */
    removeEvent: function(obj, evType, fn)
    {
      if (obj.removeEventListener)
      {
        obj.removeEventListener(evType, fn, false);
        return true;
      }
      else if (obj.detachEvent)
      {
        var r = obj.detachEvent("on"+evType, fn);
        return r;
      }
      else
      {
        return false;
      }
    },

    /**
     * Cancells the event, so that any subsequent event handlers
     * are never executed.
     * @param {Object} e
     */
    stopEvent: function(e)
    {
       e || (e = window.event);
       if (e.cancelBubble != null)
       {
          e.cancelBubble = true;
          e.returnValue = false;
       }
       else if (e)
       {
          e.preventDefault();
          e.stopPropagation();
       }

       return false;
    },

    /**
     * Parses the arguments in the rel attribute
     * @param {String} query
     */
    parseQuery: function(query)
    {
       var params = new Object();
       if (!query) return params; 
       var pairs = query.split(/[;&]/);
       var token;
       for (var i=0; i<pairs.length; i++)
       {
          var keyval = pairs[i].split('=');
          if (!keyval || keyval.length < 2) continue;
          var key = unescape(keyval.shift());
          var val = unescape(keyval.join('='));
          val = val.replace(/\+/g, ' ');
          if (val.length == 0) token = null;
          else if (val.substr(0, 1) == '"') token = '"';
          else if (val.substr(0, 1) == "'") token = "'";
          else token = null;
          if (token)
          {
            val = val.substr(1, val.length);
            if (val.substr(val.length-1,1) != token && pairs[i + 1])
            {
              do
              {
                i += 1;
                val += '&'+pairs[i];
              }
              while (pairs[i] !== undefined && pairs[i].length > 0 &&
                     pairs[i].substr(pairs[i].length-1,1) != token);
              val = val.substr(0, val.length-1);
            }
          }
          params[key] = val;
       }
       return params;
    },

    handleTag: function(e)
    {
      var t = this.getAttribute('rel');
      var params = _pub.parseQuery(t.substr(5,999));
      var url = null;
      if (params.target) url = params.target
      else if (this.target && this.target.substring(0, 1) != '_' && !params.ignore_target) url = this.target;
      else url = this.href;
      var title = this.title;
      if (_pub.inherit_frames && window.parent) window.parent.iBox.showURL(url, title, params);
      else _pub.showURL(url, title, params);
      return false;
    },
    
    plugins: {
      list: new Array(),
      register: function(func, last)
      {
        if (!last)
        {
          _pub.plugins.list = _pub.plugins.list.concat([func],_pub.plugins.list);
        }
        else
        {
          _pub.plugins.list.push(func);
        }
      }
    }
  };
  
  // private methods and variables
  var cancelled = false;
  var active_plugin = null;
  
  // events
  var events = {};

  // some containers
  // we store these in memory instead of finding them each time
  var els = {
    wrapper: null,
    footer: null,
    content: null,
    overlay: null,
    loading: null
  };

  /**
   * Creates the iBox container and appends it to an element
   * @param {Object} elem Container to attach to
   * @return {Object} iBox element
   */
  var create = function(elem)
  {
    // TODO: why isnt this using DOM tools
    // a trick on just creating an ibox wrapper then doing an innerHTML on our root ibox element
    var container = document.createElement('div');
    container.id = 'ibox';
    container.style.display = 'block';

    els.overlay = document.createElement('div');
    els.overlay.style.display = 'none';
    els.overlay.id = 'ibox_overlay';
    els.overlay.onclick = _pub.hide;
    container.appendChild(els.overlay);

    els.loading = document.createElement('div');
    els.loading.id = 'ibox_loading';
    els.loading.innerHTML = 'Loading...';
    els.loading.style.display = 'none';
    els.loading.onclick = function() {
      _pub.hide();
      cancelled = true;
    }
    container.appendChild(els.loading);

    els.wrapper = document.createElement('div')
    els.wrapper.id = 'ibox_wrapper';
    els.wrapper.style.display = 'none';

    els.content = document.createElement('div');
    els.content.id = 'ibox_content';
    els.wrapper.appendChild(els.content);
  
    var child = document.createElement('div');
    child.id = 'ibox_footer_wrapper';
  
    var child2 = document.createElement('a');
    child2.innerHTML = _pub.close_label;
    child2.href = 'javascript:void(0)';
    child2.onclick = _pub.hide;
    child.appendChild(child2);
  
    els.footer = document.createElement('div');
    els.footer.id = 'ibox_footer';
    els.footer.innerHTML = '&nbsp;';
    child.appendChild(els.footer);
    els.wrapper.appendChild(child);

    container.appendChild(els.wrapper);

    elem.appendChild(container);
    return container;
  };
  
  var hideTags = function(tag)
  {
    var list = document.getElementsByTagName(tag);
    for (var i=0; i<list.length; i++)
    {
      if (_pub.getStyle(list[i], 'visibility') != 'hidden' && list[i].style.display != 'none')
      {
        list[i].style.visibility = 'hidden';
        list[i].wasHidden = true;
      }
    }
  };
  
  var showTags = function(tag)
  {
    var list = document.getElementsByTagName(tag);
    for (var i=0; i<list.length; i++)
    {
      if (list[i].wasHidden)
      {
        list[i].style.visibility = 'visible';
        list[i].wasHidden = null;
      }
    }
  };
  
  var showInit = function(title, params, callback)
  {
    els.loading.style.display = "block";
    _pub.center(els.loading);
    
    _pub.reposition();
    if (!_pub.is_firefox) var amount = 8;
    else var amount = 10;
    for (var i=0; i<_pub.tags_to_hide.length; i++) hideTags(_pub.tags_to_hide[i]);

    //window.onscroll = _pub.reposition;
    _pub.addEvent(document, 'mousedown', _pub.check);

    // set title here
    if (title && title.length > 60) {
      title = title.substring(0, 60);
      var res = title.match(/^(.*)\s+[^\s]*$/);
      if (res != null) {
        title = res[1];
      }
      title += '&hellip;';
    }
    els.footer.innerHTML = title || "&nbsp;";

    els.overlay.style.display = "block";
    //els.overlay.style.backgroundImage = "url('" + _pub.base_url + "images/bg.png')";
    
    _pub.fadeIn(els.overlay, amount, _pub.fade_in_speed, callback);
    _pub.fireEvent('show');
  };
  
  var drawCSS = function()
  {
    // Core CSS (positioning/etc)
    var core_styles = "#ibox {z-index:1000000;} #ibox_overlay {position:absolute;top:0;left:0;right:0;z-index:1000000;} #ibox_loading {position:absolute;z-index:1000001;} #ibox_wrapper {position:absolute;top:0;left:0;z-index:1000001;} #ibox_content {z-index:1000002;overflow:auto;height:100%;position:relative;text-align:left;} #ibox_content object { display:block;} #ibox_content .ibox_image {width:100%;height:100%;margin:0;padding:0;border:0;display:block;} #ibox_footer_wrapper a {float:right;display:block;outline:0;margin:0;padding:0;} #ibox_footer_wrapper {text-align:left;position:absolute;top:5px;right:10px;left:10px;white-space:nowrap;overflow:hidden;}";
    
    // Default style/theme/skin/whatever
    var default_skin = "#ibox_footer_wrapper {font-weight:bold;}#ibox_wrapper {border:1px solid #ccc;}#ibox_loading {font-size:16px;font-weight:bold;}";

    var head = document.getElementsByTagName("head")[0];
    // tricky hack for IE
    var htmDiv = document.createElement('div');

    htmDiv.innerHTML = '<p>x</p>' +
       (default_skin ? '<style type="text/css">'+default_skin+'</style>' : '');
    head.insertBefore(htmDiv.childNodes[1], head.firstChild);

    htmDiv.innerHTML = '<p>x</p>' +
       (core_styles ? '<style type="text/css">'+core_styles+'</style>' : '');
    head.insertBefore(htmDiv.childNodes[1], head.firstChild);
  }

  var initialize = function()
  {
    // elements here start the look up from the start non <a> tags
    drawCSS();
    var els = document.getElementsByTagName("a");
    for (var i=0; i<els.length; i++)
    {
      if (els[i].getAttribute(_pub.attribute_name))
      {
        var t = els[i].getAttribute(_pub.attribute_name);
        if (t.indexOf("ibox") != -1 || t.toLowerCase() == "ibox")
        { // check if this element is an iBox element
          els[i].onclick = _pub.handleTag;
        }
      }
    }
    create(document.body);
    _pub.http = _pub.createXMLHttpRequest();
  };

  _pub.addEvent(window, 'keypress', function(e){ if (e.keyCode == (window.event ? 27 : e.DOM_VK_ESCAPE)) { iBox.hide(); }});
  _pub.addEvent(window, 'resize', _pub.reposition);
  _pub.addEvent(window, 'load', initialize);

  // DEFAULT PLUGINS

  /**
   * Handles embedded containers in the page based on url of #container.
   * This _ONLY_ works with hidden containers.
   */
  var iBoxPlugin_Container = function()
  {
    var was_error = false;
    var original_wrapper = null;
    return {
      /**
       * Matches the url and returns true if it fits this plugin.
       */
      match: function(url)
      {
        return url.indexOf('#') != -1;
      },
      /**
       * Called when this plugin is unloaded.
       */
      unload: function()
      {
        if (was_error) return;
        var elemSrc = _pub.html().firstChild;
        elemSrc.style.display = 'none';
        original_wrapper.appendChild(elemSrc);
      },
      /**
       * Handles the output
       * @param {iBox} ibox
       * @param {String} url
       * @return {iBoxContent} an instance or subclass of iBoxContent
       */
      render: function(url, params)
      {
        was_error = false;
        var elemSrcId = url.substr(url.indexOf("#") + 1);
        var elemSrc = document.getElementById(elemSrcId);
        // If the element doesnt exist, break the switch
        if (!elemSrc)
        {
          was_error = true;
          _pub.html(document.createTextNode('There was an error loading the document.'), params);
        }
        else
        {
          original_wrapper = elemSrc.parentNode;
          elemSrc.style.display = 'block';
          _pub.html(elemSrc, params);
        }
      }
    }
  }();
  _pub.plugins.register(iBoxPlugin_Container, true);

  /**
   * Handles images
   */
  var iBoxPlugin_Image = function()
  {
    // Image types (for auto detection of image display)
    var image_types = /\.jpg|\.jpeg|\.png|\.gif/gi;

    return {
      match: function(url)
      {
        return url.match(image_types);
      },

      render: function(url, params)
      {  
        var img = document.createElement('img');
        img.onclick = _pub.hide;
        img.className = 'ibox_image'
        img.style.cursor = 'pointer';
        img.onload = function()
        {
          _pub.html(img, {height: img.height, width: img.width, constrain: true})
        }
        img.onerror = function()
        {
          _pub.html(document.createTextNode('There was an error loading the document.'), params);
        }
        img.src = url;
      }
    }
  }();
  _pub.plugins.register(iBoxPlugin_Image);

  var iBoxPlugin_YouTube = function()
  {
    var youtube_url = /(?:http:\/\/)?(?:www\d*\.)?(youtube\.(?:[a-z]+))\/(?:v\/|(?:watch(?:\.php)?)?\?(?:.+&)?v=)([^&]+).*/;
    return {
      match: function(url)
      {
        return url.match(youtube_url);
      },

      render: function(url, params)
      {
        var _match = url.match(youtube_url);
        var domain = _match[1];
        var id = _match[2];
        params.width = 425;
        params.height = 355;
        params.can_resize = false;
        var html = '<div><object width="425" height="355"><param name="movie" value="http://www.' + domain + '/v/' + id + '"/><param name="wmode" value="transparent"/><embed src="http://www.' + domain + '/v/' + id + '" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"></embed></object></div>';
        _pub.html(html, params);
      }
    }
  }();
  _pub.plugins.register(iBoxPlugin_YouTube);

  var iBoxPlugin_Document = function()
  {
    return {
      match: function(url)
      {
        return true;
      },

      render: function(url, params)
      {
        if (!_pub.http) {
          // if no XMLHttpRequest, can't do URLs
          // open new browser window
          var width, height;
          if (params.width) width = parseInt(params.width);
          else width = _pub.default_width;
          if (params.height) height = parseInt(params.height);
          else height = 400;

          var wnd = window.open(url, 'ibox', 'width=' + width +
                                ',height=' + height + ',left=0,top=0' +
                                ',toolbar=no,resizable=yes,scrollbars=yes');
          wnd.focus();
        } else {
          _pub.http.open('GET', url, true);

          _pub.http.onreadystatechange = function()
          {
             if (_pub.http.readyState == 4 ||
                 // below is for post-NS/pre-FF mozilla w/ XMLHttpRequest
                 (_pub.http.readyState == 3 && _pub.is_moz &&
                  (_pub.http.status == 200 || _pub.http.status == 0) &&
                  _pub.http.responseText))
             {
               // XXX: why does status return 0?
               if (_pub.http.status == 200 || _pub.http.status == 0)
               {
                 _pub.html(_pub.http.responseText, params);
               }
               else
               {
                 _pub.html(document.createTextNode('There was an error loading the document.'), params);
               }
             }
          };
          _pub.http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
          _pub.http.send(null);
        }
      }
    };
  }();
  _pub.plugins.register(iBoxPlugin_Document);

  return _pub;
}();
