/*	Script: Waiter.js
		Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.

		Author:
		Aaron Newton - aaron [dot] newton [at] cnet [dot] com

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

		Arguments:
		target - (dom element or id) the dom element to overlay; defaults to document.body
		options - (object) a key/value set of options

		Options:
		baseHref - (string) url prefix for the img src (see below); defaults to
				'http://www.cnet.com/html/rb/assets/global/waiter/'
		img - (object) options for the image (see below)
		imgPosition - (object) options passed to <Element.setPosition> for the image; relativeTo is set to the target above automatically.
		layer - (object) options for the overlay layer (see below)
		fxOptions - (object) options passed to the effects used to transition the overlay and the image opacity

		img options:
		Properties passed to the element instantiation.
		(start code)
		//default values
		img: {
			src: 'waiter.gif',
			id: 'waitingImg',
			styles: {
				position: 'absolute',
				width: 24,
				height: 24,
				display: 'none',
				opacity: 0,
				zIndex: 999
			}
		}
		(end)

		layer options:
		Properties passed to the element instantiation.
		(start code)
		//default values
		layer:{
			id: 'waitingDiv',
			background: '#fff',
			opacity: 0.8
		}
		(end)


*/
var Waiter = new Class({
	options: {
		baseHref: 'pics/',
		containerProps: {
			styles: {
				position: 'absolute',
				display: 'none',
				opacity: 0,
				zIndex: 999
			}
		},
		msg: false,
		msgProps: {
			styles: {
				textAlign: 'center',
				fontWeight: 'bold'
			}
		},
		img: {
			src: 'waiter.gif',
			styles: {
				width: 36,
				height: 36
			}
		},
		imgPosition: {
			position: 'absolute',
			top: 570
		},
		layer:{
			styles: {
				width: 0,
				height: 0,
				position: 'absolute',
				zIndex: 998,
				display: 'none',
				opacity: 0.4,
				background: 'transparent'				
			},
			'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);
	},
/*	Property: toggle
		Toggles the waiter over the specified element. If the waiter is currently showing over the specified element, it will hide. Otherwise it will
	*/
	toggle: function(element, show) {
		//the element or the default
		element = $(element) || $(this.active) || $(this.target);
		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.stop().set({
			0: { opacity:[0]},
			1: { opacity:[0]}
		});
	},
	start: function(element){
		this.reset();
		element = $(element) || $(this.target);
		var start = function() {
			var dim = element.getComputedSize();
			this.active = element;
			this.waiterMsg.setPosition($merge(this.options.imgPosition, {
				//ralativeTo: 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(){
				if (this.acive == 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.stop();
		this.waiterFx.clearChain();
		//fade the waiter out
		this.waiterFx.start({
			0: { opacity:[0]},
			1: { opacity:[0]}
		}).chain(function(){
			this.active = null;
			this.waiterDiv.hide();
			this.waiterMsg.hide();
			this.fireEvent('onHide', this.active);
			this.callChain();
			this.clearChain();
			if ($type(callback) == "function") callback.attempt();
		}.bind(this));
		return this;
	}
});
Waiter.implement(new Options, new Events, new Chain);

/*	Class: Ajax
		Extends functionality from <Waiter> into <Ajax>.

		Additional Options:
		useWaiter - (boolean) if true will automatically apply a <Waiter> to the update target; defaults to false. Note: if you do not specify a value for update option this is ignored.
		waiterOptions - (object) options value passed on to <Waiter> class.
		waiterTarget - (element) if specified, the Waiter will overlay this element, otherwise it uses the update target specified in the ajax options.
	*/
if (typeof Ajax != "undefined") {
	//var Ajax = Ajax.extend({
	var Ajax = new Class ({
    Extends: Ajax,
		options: {
			useWaiter: false,
			waiterOptions: {},
			waiterTarget: false
		},
		initialize: function(url, 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(url, options);
			if (this.options.useWaiter && ($(this.options.update) || $(this.options.waiterTarget))) {
				this.waiter = new Waiter(this.options.waiterTarget || 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;
		}*/
	});
}
