var Waiter = new Class({
       options: {
               baseHref: 'images/',
               containerProps: {
                       styles: {
                               position: 'absolute',
                               display: 'none',
                               opacity: 0,
                               zIndex: 999
                       }
               },
               msg: false,
               msgProps: {
                       styles: {
                               textAlign: 'center',
                               fontWeight: 'bold'
                       }
               },
               img: {
                       src: 'waiter.gif',
                       styles: {
                               width: 32,
                               height: 32
                       }
               },
               imgPosition: {},
               layer:{
                       styles: {
                               width: 0,
                               height: 0,
                               position: 'absolute',
                               zIndex: 998,
                               display: 'none',
                               opacity: 0.9,
                               background: '#d1ccb9'
                       },
                       'class': 'waitingDiv'
               },
               fxOptions: {}
       },
       initialize: function(target, options){
               this.target = $(target||document.body);
               this.setOptions(options);
               this.waiterMsg = new Element('div', this.options.containerProps).injectInside(document.body).addClass("waiterDiv");
               if (this.options.msg) this.waiterMsg.adopt(new Element('div', this.options.msgProps).appendText(this.options.msg));
               this.waiterImg = $(this.options.img.id) || new Element('img', $merge(this.options.img, {
                       src: this.options.baseHref + this.options.img.src
               })).injectInside(this.waiterMsg).addClass('waiterImg');
               this.waiterDiv = $(this.options.layer.id) || new Element('div', this.options.layer).injectInside(document.body);
               this.waiterFx = this.waiterFx || new Fx.Elements($$(this.waiterMsg, this.waiterDiv), this.options.fxOptions);
       },
       toggle: function(element, show) {
               if (this.inTransit) {
                       this.chain(this.toggle.bind(this, [element, show]));
                       return this;
               }
               element = $(element) || $(this.active) || $(this.target);
               if (!$(element)) return this;
               if (this.active && element != this.active) return this.stop().chain(this.start.bind(this, element));
               if((!this.active || show) && show !== false) this.start(element);
               else if(this.active && !show) this.stop();
               return this;
       },
       start: function(element){
               if (this.inTransit) {
                       this.chain(this.start.bind(this, element));
                       return this;
               }
               this.inTransit = true;
               element = $(element) || $(this.target);
               var start = function() {
                       var dim = element.getComputedSize();
                       this.active = element;
                       this.waiterMsg.setPosition($merge(this.options.imgPosition, {
                               relativeTo: element
                       })).show();
                       this.waiterDiv.setStyles({
                               width: dim.totalWidth,
                               height: dim.totalHeight,
                               display: 'block'
                       }).setPosition({
                               relativeTo: element,
                               position: 'upperLeft'
                       });
                       this.waiterFx.start({
                               0: { opacity:[1] },
                               1: { opacity:[this.options.layer.styles.opacity]}
                       }).chain(function(){
                               this.inTransit = false;
                               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.inTransit) {
                       this.chain(this.stop.bind(this, callback));
                       return this;
               }
               if (!this.active) return this;
               this.inTransit = true;
               this.waiterFx.start({
                       0: { opacity:[0]},
                       1: { opacity:[0]}
               }).chain(function(){
                       this.inTransit = false;
                       this.active = null;
                       this.waiterDiv.hide();
                       this.waiterMsg.hide();  
                       this.fireEvent('onHide', this.active);
                       this.callChain();
                       if ($type(callback) == "function") callback.attempt();
               }.bind(this));
               return this;
       }
});
Waiter.implement(new Options, new Events, new Chain);
if (typeof Ajax != "undefined") {
       var Ajax = Ajax.extend({
               options: {
                       useWaiter: false,
                       waiterOptions: {}
               },
               initialize: function(url, options){
                       this.parent(url, options);
                       if (this.options.useWaiter && this.options.update) {
                               this.waiter = new Waiter(this.options.update, this.options.waiterOptions);
                               this.addEvent('onComplete', this.waiter.stop.bind(this.waiter));
                               this.addEvent('onFailure', this.waiter.stop.bind(this.waiter));
                       }
               },
               request: function(data) {
                       if (this.waiter) this.waiter.start().chain(this.parent.bind(this, data));
                       else this.parent(data);
                       return this;
               }
       });
}
