/*
Script: IframeShim.js
  Defines IframeShim, a class for obscuring select lists and flash objects in IE.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/

function showPopup(editid, divid, params){
    var p = $merge({width: 600, title:'', cornerHandle: true}, params||{});
    if ($(editid)) var position = 'bottomleft';
    else var position = 'center';
    var stickywin = $merge({
        relativeTo: $(editid),
        position: position,
        offset:{x:-10},
        allowMultiple: false,
        draggable: true
      }, params);

    var content = $(divid);
    if (content) content.show();
    else content = divid;
    //xd(stickywin);
    popups["_last_"] = new StickyWinFxModal($merge(stickywin, {
      'content': StickyWin.ui(p["title"], content, p)
    }));
    return popups["_last_"];
}

function popupImg(el, src, params){
  if (!(params instanceof Object))
    params ={title: src};
  //params["width"] =
  var img = new Asset.image(src, {onload: function(){
    params['width']=this.get('width').toInt()+50;
    showPopup(el, img, params);
  }});
  //var img = new Element("img", {src:src})
  //el.popup.defaultSize(el.popup.content.firstChild.width, el.popup.content.firstChild.height);
}

/*
Script: modalizer.js
  Defines Modalizer: functionality to overlay the window contents with a semi-transparent layer that prevents interaction with page content until it is removed

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/
var Modalizer = new Class({
  defaultModalStyle: {
    display:'block',
    position:'fixed',
    top:0,
    left:0,
    'z-index':5000,
    'background-color':'#333',
    opacity:0.8
  },
  setModalOptions: function(options){
    this.modalOptions = $merge({
      width:(window.getScrollSize().x+300),
      height:(window.getScrollSize().y+300),
      elementsToHide: 'select',
      hideOnClick: true,
      modalStyle: {},
      updateOnResize: true,
      layerId: 'modalOverlay',
      onModalHide: $empty,
      onModalShow: $empty
    }, this.modalOptions, options);
    return this;
  },
  toElement: function(){
    if (!this.modalOptions.layerId) this.setModalOptions();
    return $(this.modalOptions.layerId) || new Element('div', {id: this.modalOptions.layerId}).inject(document.body);;
  },
  resize: function(){
    if($(this)) {
      $(this).setStyles({
        width:(window.getScrollSize().x+300),
        height:(window.getScrollSize().y+300)
      });
    }
  },
  setModalStyle: function (styleObject){
    this.modalOptions.modalStyle = styleObject;
    this.modalStyle = $merge(this.defaultModalStyle, {
      width:this.modalOptions.width,
      height:this.modalOptions.height
    }, styleObject);
    if($(this)) $(this).setStyles(this.modalStyle);
    return(this.modalStyle);
  },
  modalShow: function(options){
    this.setModalOptions(options);
    $(this).setStyles(this.setModalStyle(this.modalOptions.modalStyle));
    if(Browser.Engine.trident4) $(this).setStyle('position','absolute');
    $(this).removeEvents('click').addEvent('click', function(){
      this.modalHide(this.modalOptions.hideOnClick);
    }.bind(this));
    this.bound = this.bound||{};
    if(!this.bound.resize && this.modalOptions.updateOnResize) {
      this.bound.resize = this.resize.bind(this);
      window.addEvent('resize', this.bound.resize);
    }
    if ($type(this.modalOptions.onModalShow)  == "function") this.modalOptions.onModalShow();
    this.togglePopThroughElements(0);
    $(this).setStyle('display','block');
    return this;
  },
  modalHide: function(override, force){
    if(override === false) return false; //this is internal, you don't need to pass in an argument
    this.togglePopThroughElements(1);
    if ($type(this.modalOptions.onModalHide) == "function") this.modalOptions.onModalHide();
    $(this).setStyle('display','none');
    if(this.modalOptions.updateOnResize) {
      this.bound = this.bound||{};
      if(!this.bound.resize) this.bound.resize = this.resize.bind(this);
      window.removeEvent('resize', this.bound.resize);
    }
    return this;
  },
  togglePopThroughElements: function(opacity){
    if(Browser.Engine.trident4 || (Browser.Engine.gecko && Browser.Platform.mac)) {
      $$(this.modalOptions.elementsToHide).each(function(sel){
        sel.setStyle('opacity', opacity);
      });
    }
  }
});


/*
Script: StickyWin.js

Creates a div within the page with the specified contents at the location relative to the element you specify; basically an in-page popup maker.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/

var StickyWin = new Class({
  Implements: [Options, Events, StyleWriter],
  options: {
//  onDisplay: $empty,
//  onClose: $empty,
    closeClassName: 'closeSticky',
    pinClassName: 'pinSticky',
    content: '',
    zIndex: 10000,
    className: '',
    //id: ... set above in initialize function
/*  these are the defaults for setPosition anyway
/************************************************
//    edge: false, //see Element.setPosition in element.cnet.js
//    position: 'center', //center, corner == upperLeft, upperRight, bottomLeft, bottomRight
//    offset: {x:0,y:0},
//    relativeTo: document.body, */
    width: false,
    height: false,
    timeout: -1,
    allowMultipleByClass: false,
    allowMultiple: true,
    showNow: true,
    useIframeShim: true,
    iframeShimSelector: ''
  },
  css: '.SWclearfix:after {content: "."; display: block; height: 0; clear: both; visibility: hidden;}'+
       '.SWclearfix {display: inline-table;}'+
       '* html .SWclearfix {height: 1%;}'+
       '.SWclearfix {display: block;}',
  initialize: function(options){
    this.options.inject = {
      target: document.body,
      where: 'bottom'
    };
    this.setOptions(options);

    this.id = this.options.id || 'StickyWin_'+new Date().getTime();
    this.makeWindow();
    if(this.options.content) this.setContent(this.options.content);
    if(this.options.timeout > 0) {
      this.addEvent('onDisplay', function(){
        this.hide.delay(this.options.timeout, this)
      }.bind(this));
    }
    if(this.options.showNow) this.show();
    //add css for clearfix
    this.createStyle(this.css, 'StickyWinClearFix');
  },
  toElement: function() {
    return this.win;
  },
  makeWindow: function(){
    this.destroyOthers();
    if(!$(this.id)) {
      this.win = new Element('div', {
        id:   this.id
      }).addClass(this.options.className).addClass('StickyWinInstance').addClass('SWclearfix').setStyles({
        display:'none',
        position:'absolute',
        zIndex:this.options.zIndex
      }).inject(this.options.inject.target, this.options.inject.where).store('StickyWin', this);
    } else this.win = $(this.id);
    if(this.options.width && $type(this.options.width.toInt())=="number") this.win.setStyle('width', this.options.width.toInt());
    if(this.options.height && $type(this.options.height.toInt())=="number") this.win.setStyle('height', this.options.height.toInt());
    return this;
  },
  show: function(){
    this.fireEvent('onDisplay');
    this.showWin();
    if(this.options.useIframeShim) this.showIframeShim();
    this.visible = true;
    return this;
  },
  showWin: function(){
    this.win.setStyle('display','block');
    if(!this.positioned) this.position();
  },
  hide: function(suppressEvent){
    if(!suppressEvent) this.fireEvent('onClose');
    this.hideWin();
    if(this.options.useIframeShim) this.hideIframeShim();
    this.visible = false;
    return this;
  },
  hideWin: function(){
    this.win.setStyle('display','none');
  },
  destroyOthers: function() {
    if(!this.options.allowMultipleByClass || !this.options.allowMultiple) {
      $$('div.StickyWinInstance').each(function(sw) {
        if(!this.options.allowMultiple || (!this.options.allowMultipleByClass && sw.hasClass(this.options.className)))
          sw.dispose();
      }, this);
    }
  },
  setContent: function(html) {
    if(this.win.getChildren().length>0) this.win.empty();
    if($type(html) == "string") this.win.set('html', html);
    else if ($(html)) this.win.adopt(html);
    this.win.getElements('.'+this.options.closeClassName).each(function(el){
      el.addEvent('click', this.hide.bind(this));
    }, this);
    this.win.getElements('.'+this.options.pinClassName).each(function(el){
      el.addEvent('click', this.togglepin.bind(this));
    }, this);
    return this;
  },
  position: function(){
    this.positioned = true;
    this.win.setPosition({
      relativeTo: this.options.relativeTo,
      position: this.options.position,
      offset: this.options.offset,
      edge: this.options.edge
    });
    if(this.shim) this.shim.position();
    return this;
  },
  pin: function(pin) {
    if(!this.win.pin) {
      dbug.log('you must include element.pin.js!');
      return this;
    }
    this.pinned = $pick(pin, true);
    this.win.pin(pin);
    return this;
  },
  unpin: function(){
    return this.pin(false);
  },
  togglepin: function(){
    return this.pin(!this.pinned);
  },
  makeIframeShim: function(){
    if(!this.shim){
      var el = (this.options.iframeShimSelector)?this.win.getElement(this.options.iframeShimSelector):this.win;
      this.shim = new IframeShim(el, {
        display: false,
        name: 'StickyWinShim'
      });
    }
  },
  showIframeShim: function(){
    if(this.options.useIframeShim) {
      this.makeIframeShim();
      this.shim.show();
    }
  },
  hideIframeShim: function(){
    if(this.options.useIframeShim)
      this.shim.hide();
  },
  destroy: function(){
    if (this.win) this.win.dispose();
    if(this.options.useIframeShim) this.shim.dispose();
    if($('modalOverlay'))$('modalOverlay').dispose();
  }
});


/*
Script: StickyWinFx.js

Extends StickyWin to create popups that fade in and out and can be dragged and resized (requires StickyWinFx.Drag.js).

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/
var StickyWinFx = new Class({
  Extends: StickyWin,
  options: {
    fade: true,
    fadeDuration: 150,
//  fadeTransition: 'sine:in:out',
    draggable: false,
    dragOptions: {},
    dragHandleSelector: '.dragHandle',
    resizable: false,
    resizeOptions: {},
    resizeHandleSelector: ''
  },
  setContent: function(html){
    this.parent(html);
    if(this.options.draggable) this.makeDraggable();
    if(this.options.resizable) this.makeResizable();
    return this;
  },
  hideWin: function(){
    if(this.options.fade) this.fade(0);
    else this.parent();
  },
  showWin: function(){
    if(this.options.fade) this.fade(1);
    else this.parent();
  },
  fade: function(to){
    if(!this.fadeFx) {
      this.win.setStyles({
        opacity: 0,
        display: 'block'
      });
      var opts = {
        property: 'opacity',
        duration: this.options.fadeDuration
      };
      if (this.options.fadeTransition) opts.transition = this.options.fadeTransition;
      this.fadeFx = new Fx.Tween(this.win, opts);
    }
    if (to > 0) {
      this.win.setStyle('display','block');
      this.position();
    }
    this.fadeFx.clearChain();
    this.fadeFx.start(to).chain(function (){
      if(to == 0) this.win.setStyle('display', 'none');
    }.bind(this));
    return this;
  },
  makeDraggable: function(){
    dbug.log('you must include Drag.js, cannot make draggable');
  },
  makeResizable: function(){
    dbug.log('you must include Drag.js, cannot make resizable');
  }
});

/*
Script: StickyWinFx.Drag.js

Implements drag and resize functionaity into StickyWinFx. See StickyWinFx for the options.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/

if(typeof Drag != "undefined"){
  StickyWinFx.implement({
    makeDraggable: function(){
      var toggled = this.toggleVisible(true);
      if(this.options.useIframeShim) {
        this.makeIframeShim();
        var onComplete = (this.options.dragOptions.onComplete || $empty);
        this.options.dragOptions.onComplete = function(){
          onComplete();
          this.shim.position();
        }.bind(this);
      }
      if(this.options.dragHandleSelector) {
        var handle = this.win.getElement(this.options.dragHandleSelector);
        if (handle) {
          handle.setStyle('cursor','move');
          this.options.dragOptions.handle = handle;
        }
      }
      this.win.makeDraggable(this.options.dragOptions);
      if (toggled) this.toggleVisible(false);
    },
    makeResizable: function(){
      var toggled = this.toggleVisible(true);
      if(this.options.useIframeShim) {
        this.makeIframeShim();
        var onComplete = (this.options.resizeOptions.onComplete || $empty);
        this.options.resizeOptions.onComplete = function(){
          onComplete();
          this.shim.position();
        }.bind(this);
      }
      if(this.options.resizeHandleSelector) {
        var handle = this.win.getElement(this.options.resizeHandleSelector);
        if(handle) this.options.resizeOptions.handle = this.win.getElement(this.options.resizeHandleSelector);
      }
      this.win.makeResizable(this.options.resizeOptions);
      if (toggled) this.toggleVisible(false);
    },
    toggleVisible: function(show){
      if(!this.visible && Browser.Engine.webkit && $pick(show, true)) {
        this.win.setStyles({
          display: 'block',
          opacity: 0
        });
        return true;
      } else if(!$pick(show, false)){
        this.win.setStyles({
          display: 'none',
          opacity: 1
        });
        return false;
      }
      return false;
    }
  });
}


/*
Script: StickyWin.Modal.js

This script extends StickyWin and StickyWinFx classes to add Modalizer functionality.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/
var StickyWinModal, StickyWinFxModal;
(function(){
var modalWinBase = function(extend){
  return {
    Extends: extend,
    initialize: function(options){
      options = options||{};
      this.setModalOptions($merge(options.modalOptions||{}, {
        onModalHide: function(){
            this.hide(false);
          }.bind(this)
        }));
      this.parent(options);
    },
    show: function(showModal){
      if($pick(showModal, true)) {
        this.modalShow();
        this.win.getElements(this.modalOptions.elementsToHide).setStyle('opacity', 1);
      }
      this.parent();
    },
    hide: function(hideModal){
      if($pick(hideModal, true)) this.modalHide();
      this.parent($pick(hideModal, true));
    }
  }
};
StickyWinModal = new Class(modalWinBase(StickyWin));
StickyWinModal.implement(new Modalizer);
StickyWinFxModal = (typeof StickyWinFx != "undefined")?new Class(modalWinBase(StickyWinFx)):$empty;
try { StickyWinFxModal.implement(new Modalizer()); }catch(e){}
})();

/*
Script: StickyWin.alert.js
  Defines StickyWin.alert, a simple little alert box with a close button.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/
StickyWin.alert = function(msghdr, msg, hideOnClick, baseHref) {
  baseHref = baseHref||"/cnet/html/rb/assets/global/simple.error.popup";
  msg = '<p class="errorMsg SWclearfix" style="margin: 0px;">' +
            '<img src="'+baseHref+'/icon_problems_sm.gif"'+
            ' class="bang clearfix" style="float: left; width: 30px; height: 30px; margin: 3px 5px 5px 0px;">'
             + msg + '</p>';
  var body = StickyWin.ui(msghdr, msg, {width: 350});
  return new StickyWinModal({
    modalOptions: {
      modalStyle: {
        zIndex: 11000,
        hideOnClick: hideOnClick
      }
    },
    zIndex: 110001,
    content: body,
    position: 'center' //center, corner
  });
};

/*
Script: StickyWin.ui.js

Creates an html holder for in-page popups using a default style.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/

StickyWin.ui = function(caption, body, options){
  options = $extend({
    width: 300,
    css: "div.DefaultStickyWin div.body{font-family:verdana; font-size:11px; line-height: 13px;}"+
      "div.DefaultStickyWin div.top_ul{background:url({%baseHref%}full.png) top left no-repeat; height:30px; width:15px; float:left}"+
      "div.DefaultStickyWin div.top_ur{position:relative; left:0px !important; left:-4px; background:url({%baseHref%}full.png) top right !important; height:30px; margin:0px 0px 0px 15px !important; margin-right:-4px; padding:0px}"+
      "div.DefaultStickyWin h1.caption{clear: none !important; margin:0px 5px 0px 0px !important; overflow: hidden; padding:0 !important; font-weight:bold; color:#555; font-size:14px !important; position:relative; top:8px !important; left:5px !important; float: left; height: 22px !important;}"+
      "div.DefaultStickyWin div.middle, div.DefaultStickyWin div.closeBody {background:url({%baseHref%}body.png) top left repeat-y; margin:0px 20px 0px 0px !important; margin-bottom: -3px; position: relative;  top: 0px !important; top: -3px;}"+
      "div.DefaultStickyWin div.body{background:url({%baseHref%}body.png) top right repeat-y; padding:8px 30px 8px 0px !important; margin-left:5px !important; position:relative; right:-20px !important;}"+
      "div.DefaultStickyWin div.bottom{clear:both}"+
      "div.DefaultStickyWin div.bottom_ll{background:url({%baseHref%}full.png) bottom left no-repeat; width:15px; height:15px; float:left}"+
      "div.DefaultStickyWin div.bottom_lr{background:url({%baseHref%}full.png) bottom right; position:relative; left:0px !important; left:-4px; margin:0px 0px 0px 15px !important; margin-right:-4px; height:15px}"+
      "div.DefaultStickyWin div.closeButtons{text-align: center; background:url({%baseHref%}body.png) top right repeat-y; padding: 0px 30px 8px 0px; margin-left:5px; position:relative; right:-20px}"+
      "div.DefaultStickyWin a.button:hover{background:url({%baseHref%}big_button_over.gif) repeat-x}"+
      "div.DefaultStickyWin a.button {background:url({%baseHref%}big_button.gif) repeat-x; margin: 2px 8px 2px 8px; padding: 2px 12px; cursor:pointer; border: 1px solid #999 !important; text-decoration:none; color: #000 !important;}"+
      "div.DefaultStickyWin div.closeButton{width:13px; height:13px; background:url({%baseHref%}closebtn.gif) no-repeat; position: absolute; right: 0px; margin:10px 15px 0px 0px !important; cursor:pointer}"+
      "div.DefaultStickyWin div.dragHandle {  width: 11px;  height: 25px; position: relative; top: 5px; left: -3px; cursor: move; background: url({%baseHref%}drag_corner.gif); float: left;}",
    cornerHandle: false,
    cssClass: '',
    baseHref: '/cnet/html/rb/assets/global/stickyWinHTML/',
    buttons: []
/*  These options are deprecated:
    closeTxt: false,
    onClose: $empty,
    confirmTxt: false,
    onConfirm: $empty */
  }, options);
  //legacy support
  if(options.confirmTxt) options.buttons.push({text: options.confirmTxt, onClick: options.onConfirm || $empty});
  if(options.closeTxt) options.buttons.push({text: options.closeTxt, onClick: options.onClose || $empty});

  new StyleWriter().createStyle(options.css.substitute({baseHref: options.baseHref}, /\\?\{%([^}]+)%\}/g), 'defaultStickyWinStyle');
  caption = $pick(caption, '%caption%');
  body = $pick(body, '%body%');
  var container = new Element('div').setStyle('width', options.width).addClass('DefaultStickyWin');
  if(options.cssClass) container.addClass(options.cssClass);
  //header
  var h1Caption = new Element('h1').addClass('caption').setStyle('width', (options.width.toInt()-(options.cornerHandle?70:60)));

  if($(caption)) h1Caption.adopt(caption);
  else h1Caption.set('html', caption);

  var bodyDiv = new Element('div').addClass('body');

  //console.log(body);

  if($(body)) bodyDiv.adopt(body);
  else bodyDiv.set('html', body);

  var top_ur = new Element('div').addClass('top_ur').adopt(
      new Element('div').addClass('closeButton').addClass('closeSticky')
    ).adopt(h1Caption);
  if(options.cornerHandle) new Element('div').addClass('dragHandle').inject(top_ur, 'top');
  else h1Caption.addClass('dragHandle');
  container.adopt(
    new Element('div').addClass('top').adopt(
        new Element('div').addClass('top_ul')
      ).adopt(top_ur)
  );
  //body
  container.adopt(new Element('div').addClass('middle').adopt(bodyDiv));
  //close buttons
  if(options.buttons.length > 0){
    var closeButtons = new Element('div').addClass('closeButtons');
    options.buttons.each(function(button){
      if(button.properties && button.properties.className){
        button.properties['class'] = button.properties.className;
        delete button.properties.className;
      }
      var properties = $merge({'class': 'closeSticky'}, button.properties);
      new Element('a').addEvent('click',
        button.onClick || $empty).appendText(
        button.text).inject(closeButtons).setProperties(properties).addClass('button');
    });
    container.adopt(new Element('div').addClass('closeBody').adopt(closeButtons));
  }
  //footer
  container.adopt(
    new Element('div').addClass('bottom').adopt(
        new Element('div').addClass('bottom_ll')
      ).adopt(
        new Element('div').addClass('bottom_lr')
    )
  );
  return container;
};


/*
Script: Waiter.js

Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.

License:
  http://clientside.cnet.com/wiki/cnet-libraries#license
*/

var Waiter = new Class({
  Implements: [Options, Events, Chain],
  options: {
    baseHref: '/cnet/html/rb/assets/global/waiter/',
    containerProps: {
      styles: {
        position: 'absolute',
        'text-align': 'center'
      },
      'class':'waiterContainer'
    },
    containerPosition: {},
    msg: false,
    msgProps: {
      styles: {
        'text-align': 'center',
        fontWeight: 'bold'
      },
      'class':'waiterMsg'
    },
    img: {
      src: 'waiter.gif',
      styles: {
        width: 24,
        height: 24
      },
      'class':'waiterImg'
    },
    layer:{
      styles: {
        width: 0,
        height: 0,
        position: 'absolute',
        zIndex: 999,
        display: 'none',
        opacity: 0.9,
        background: '#fff'
      },
      'class': 'waitingDiv'
    },
    useIframeShim: true,
    fxOptions: {},
    injectWhere: null
//  iframeShimOptions: {},
//  onShow: $empty
//  onHide: $empty
  },
  initialize: function(target, options){
    this.target = $(target)||$(document.body);
    this.setOptions(options);
    this.waiterContainer = new Element('div', this.options.containerProps);
    if (this.options.msg) {
      this.msgContainer = new Element('div', this.options.msgProps);
      this.waiterContainer.adopt(this.msgContainer);
      if (!$(this.options.msg)) this.msg = new Element('p').appendText(this.options.msg);
      else this.msg = $(this.options.msg);
      this.msgContainer.adopt(this.msg);
    }
    if (this.options.img) this.waiterImg = $(this.options.img.id) || new Element('img').inject(this.waiterContainer);
    this.waiterOverlay = $(this.options.layer.id) || new Element('div').adopt(this.waiterContainer);
    this.waiterOverlay.set(this.options.layer);
    this.place(target);
    try {
      if (this.options.useIframeShim) this.shim = new IframeShim(this.waiterOverlay, this.options.iframeShimOptions);
    } catch(e) {
      dbug.log("Waiter attempting to use IframeShim but failed; did you include IframeShim? Error: ", e);
      this.options.useIframeShim = false;
    }
    this.waiterFx = this.waiterFx || new Fx.Elements($$(this.waiterContainer, this.waiterOverlay), this.options.fxOptions);
  },
  place: function(target, where){
    var where = where || this.options.injectWhere || target == document.body ? 'inside' : 'after';
    this.waiterOverlay.inject(target, where);
  },
  toggle: function(element, show) {
    //the element or the default
    element = $(element) || $(this.active) || $(this.target);
    this.place(element);
    if (!$(element)) return this;
    if (this.active && element != this.active) return this.stop(this.start.bind(this, element));
    //if it's not active or show is explicit
    //or show is not explicitly set to false
    //start the effect
    if((!this.active || show) && show !== false) this.start(element);
    //else if it's active and show isn't explicitly set to true
    //stop the effect
    else if(this.active && !show) this.stop();
    return this;
  },
  reset: function(){
    this.waiterFx.cancel().set({
      0: { opacity:[0]},
      1: { opacity:[0]}
    });
  },
  start: function(element){
    this.reset();
    element = $(element) || $(this.target);
    this.place(element);
    if (this.options.img) {
      this.waiterImg.set($merge(this.options.img, {
        src: this.options.baseHref + this.options.img.src
      }));
    }

    var start = function() {
      var dim = element.getComputedSize();
      this.active = element;
      this.waiterOverlay.setStyles({
        width: this.options.layer.width||dim.totalWidth,
        height: this.options.layer.height||dim.totalHeight,
        display: 'block'
      }).setPosition({
        relativeTo: element,
        position: 'upperLeft'
      });
      this.waiterContainer.setPosition($merge({
        relativeTo: this.waiterOverlay
      }, this.options.containerPosition));
      if (this.options.useIframeShim) this.shim.show();
      this.waiterFx.start({
        0: { opacity:[1] },
        1: { opacity:[this.options.layer.styles.opacity]}
      }).chain(function(){
        if (this.active == element) this.fireEvent('onShow', element);
        this.callChain();
      }.bind(this));
    }.bind(this);

    if (this.active && this.active != element) this.stop(start);
    else start();

    return this;
  },
  stop: function(callback){
    if (!this.active) {
      if ($type(callback) == "function") callback.attempt();
      return this;
    }
    this.waiterFx.cancel();
    this.waiterFx.clearChain();
    //fade the waiter out
    this.waiterFx.start({
      0: { opacity:[0]},
      1: { opacity:[0]}
    }).chain(function(){
      this.active = null;
      this.waiterOverlay.hide();
      if (this.options.useIframeShim) this.shim.hide();
      this.fireEvent('onHide', this.active);
      this.callChain();
      this.clearChain();
      if ($type(callback) == "function") callback.attempt();
    }.bind(this));
    return this;
  }
});

if (typeof Request != "undefined" && Request.HTML) {
  Request.HTML = Class.refactor(Request.HTML, {
    options: {
      useWaiter: false,
      waiterOptions: {},
      waiterTarget: false
    },
    initialize: function(options){
      this._send = this.send;
      this.send = function(options){
        if(this.waiter) this.waiter.start().chain(this._send.bind(this, options));
        else this._send(options);
        return this;
      };
      this.parent(options);
      if (this.options.useWaiter && ($(this.options.update) || $(this.options.waiterTarget))) {
        this.waiter = new Waiter(this.options.waiterTarget || this.options.update, this.options.waiterOptions);
        ['onComplete', 'onException', 'onCancel'].each(function(event){
          this.addEvent(event, this.waiter.stop.bind(this.waiter));
        }, this);
      }
    }
  });
}

Element.Properties.waiter = {

  set: function(options){
    var waiter = this.retrieve('waiter');
    return this.eliminate('wait').store('waiter:options');
  },

  get: function(options){
    if (options || !this.retrieve('waiter')){
      if (options || !this.retrieve('waiter:options')) this.set('waiter', options);
      this.store('waiter', new Waiter(this, this.retrieve('waiter:options')));
    }
    return this.retrieve('waiter');
  }

};

Element.implement({

  wait: function(options){
    this.get('waiter', options).start();
    return this;
  },

  release: function(){
    var opt = Array.link(arguments, {options: Object.type, callback: Function.type});
    this.get('waiter', opt.options).stop(opt.callback);
    return this;
  }

});
