/**
 * @author Ryan Johnson <http://saucytiger.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/core
 * @require prototype.js
 */

if(typeof(Control) == 'undefined')
	Control = {};
	
if(typeof($break) == "undefined")
	$break = {};

var $proc = function(proc){
	return typeof(proc) == 'function' ? proc : function(){return proc};
};

var $value = function(value){
	return typeof(value) == 'function' ? value() : value;
};

Object.Event = 
{
    extend: function(object)
    {
        object.makeObservable = function(method_name)
        {
            if (this[method_name]) 
            {
                this[method_name] = this[method_name].wrap(function(proceed)
                {
                    var args = $A(arguments).slice(1);
                    var response = proceed.apply(this, args);
                    args.unshift(method_name);
                    this.notify.apply(this, args);
                    return response;
                });
            }
            if (this.prototype) 
            {
				this.prototype.makeObservable(method_name);
			}    
        };
		
        object.observeMethod = function(method_name, observer, scope)
        {
            return new Object.Event.MethodCallObserver([[this, method_name]], observer, scope);
        };
		
        object._objectEventSetup = function(event_name)
        {
            this._observers = this._observers || {};
            this._observers[event_name] = this._observers[event_name] || [];
        };
		
        object.observe = function(event_name, observer)
        {
            if (typeof(event_name) == 'string' && typeof(observer) != 'undefined') 
            {
                this._objectEventSetup(event_name);
				
                if (!this._observers[event_name].include(observer))
				{
                    this._observers[event_name].push(observer);
				} 
            }
            else if (typeof(event_name) == 'object' && event_name.each && typeof(observer) != 'undefined') 
            {
                event_name.each(function(event)
                {
                    this._objectEventSetup(event);

                    if (!this._observers[event].include(observer))
					{
                        this._observers[event].push(observer);
					} 
                }.bind(this))
            }
            else
			{
                for (var e in event_name)
				{
                    this.observe(e, event_name[e]);
				} 
			}
        };

        object.stopObserving = function(event_name, observer)
        {
            this._objectEventSetup(event_name);

            if (event_name && observer)
			{
                this._observers[event_name] = this._observers[event_name].without(observer);
			} 
            else if (event_name)
			{
                this._observers[event_name] = [];
			} 
            else
			{
                this._observers = {};
			} 
        };

        object.observeOnce = function(event_name, outer_observer)
        {
            var inner_observer = function()
            {
                outer_observer.apply(this, arguments);
                this.stopObserving(event_name, inner_observer);
            }.bind(this);
            
			this._objectEventSetup(event_name);
            this._observers[event_name].push(inner_observer);
        };
		
        object.notify = function(event_name)
        {
        
            /*if (Portal.Vars.debugMode) 
            {
                console.info('Notify: ' + event_name);
            }*/
            
            this._objectEventSetup(event_name);
            var collected_return_values = [];
            var args = $A(arguments).slice(1);
        
		    try 
            {
                for (var i = 0; i < this._observers[event_name].length; ++i) 
                    collected_return_values.push(this._observers[event_name][i].apply(this._observers[event_name][i], args) || null);
            } 
            catch (e) 
            {
                if (e == $break)
				{
                    return false;
				} 
                else
				{
                    throw e;
				} 
            }
            return collected_return_values;
        };
		
        if (object.prototype) 
        {
            object.prototype.makeObservable = object.makeObservable;
            object.prototype.observeMethod = object.observeMethod;
            object.prototype._objectEventSetup = object._objectEventSetup;
            object.prototype.observe = object.observe;
            object.prototype.stopObserving = object.stopObserving;
            object.prototype.observeOnce = object.observeOnce;
           
		    object.prototype.notify = function(event_name)
            {
                if (object.notify) 
                {
                    var args = $A(arguments).slice(1);
                    args.unshift(this);
                    args.unshift(event_name);
                    object.notify.apply(object, args);
                }
            
			    this._objectEventSetup(event_name);
                var args = $A(arguments).slice(1);
                var collected_return_values = [];
            
			    try 
                {
                    if (this.options && this.options[event_name] && typeof(this.options[event_name]) == 'function')
					{
                        collected_return_values.push(this.options[event_name].apply(this, args) || null);
					} 
					
                    for (var i = 0; i < this._observers[event_name].length; ++i)
					{
                        collected_return_values.push(this._observers[event_name][i].apply(this._observers[event_name][i], args) || null);
					} 
                } 
                catch (e) 
                {
                    if (e == $break)
					{
                        return false;
					} 
                    else
					{
                        throw e;
					} 
                }
				
                return collected_return_values;
            };
        }
    },
    MethodCallObserver: Class.create(
    {
        initialize: function(methods, observer, scope)
        {
            this.methods = methods;
            this.originals = [];
			
            this.methods.each(function(method)
            {
                this.originals.push(method[0][method[1]]);
            
			    method[0][method[1]] = method[0][method[1]].wrap(function(proceed)
                {
                    var args = $A(arguments).slice(1);
                    observer.apply(this, args);
                    return proceed.apply(this, args);
                });
            }.bind(this));
            
			if (scope) 
            {
                scope();
                this.stop();
            }
        },
        stop: function()
        {
            this.methods.each(function(method, i)
            {
                method[0][method[1]] = this.originals[i];
            }.bind(this));
        }
    })
};

/* Begin Core Extensions */

//Element.observeOnce
Element.addMethods({
	observeOnce: function(element,event_name,outer_callback){
		var inner_callback = function(){
			outer_callback.apply(this,arguments);
			Element.stopObserving(element,event_name,inner_callback);
		};
		Element.observe(element,event_name,inner_callback);
	}
});

//mouseenter, mouseleave
//from http://dev.rubyonrails.org/attachment/ticket/8354/event_mouseenter_106rc1.patch
Object.extend(Event, (function() {
	var cache = Event.cache;

	function getEventID(element) {
		
		if(!element)
		{
			return null;
		}
		
		if (element._prototypeEventID) return element._prototypeEventID[0];
		arguments.callee.id = arguments.callee.id || 1;
		return element._prototypeEventID = [++arguments.callee.id];
	}

	function getDOMEventName(eventName) {
		if (eventName && eventName.include(':')) return "dataavailable";
		//begin extension
		if(!Prototype.Browser.IE){
			eventName = {
				mouseenter: 'mouseover',
				mouseleave: 'mouseout'
			}[eventName] || eventName;
		}
		//end extension
		return eventName;
	}

	function getCacheForID(id) {
		return cache[id] = cache[id] || { };
	}

	function getWrappersForEventName(id, eventName) {
		var c = getCacheForID(id);
		return c[eventName] = c[eventName] || [];
	}

	function createWrapper(element, eventName, handler) {
		var id = getEventID(element);
		var c = getWrappersForEventName(id, eventName);
		if (c.pluck("handler").include(handler)) return false;

		var wrapper = function(event) {
			if (!Event || !Event.extend ||
				(event.eventName && event.eventName != eventName))
					return false;

			Event.extend(event);
			handler.call(element, event);
		};
		
		//begin extension
		if(!(Prototype.Browser.IE) && ['mouseenter','mouseleave'].include(eventName)){
			wrapper = wrapper.wrap(function(proceed,event) {	
				var rel = event.relatedTarget;
				var cur = event.currentTarget;			 
				if(rel && rel.nodeType == Node.TEXT_NODE)
					rel = rel.parentNode;	  
				if(rel && rel != cur && !rel.descendantOf(cur))	  
					return proceed(event);   
			});	 
		}
		//end extension

		wrapper.handler = handler;
		c.push(wrapper);
		return wrapper;
	}

	function findWrapper(id, eventName, handler) {
		var c = getWrappersForEventName(id, eventName);
		return c.find(function(wrapper) { return wrapper.handler == handler });
	}

	function destroyWrapper(id, eventName, handler) {
		var c = getCacheForID(id);
		if (!c[eventName]) return false;
		c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
	}

	function destroyCache() {
		for (var id in cache)
			for (var eventName in cache[id])
				cache[id][eventName] = null;
	}

	if (window.attachEvent) {
		window.attachEvent("onunload", destroyCache);
	}

	return {
		observe: function(element, eventName, handler) {
			element = $(element);
			var name = getDOMEventName(eventName);

			var wrapper = createWrapper(element, eventName, handler);
			if (!wrapper) return element;

			if (element.addEventListener) {
				element.addEventListener(name, wrapper, false);
			} else {
				element.attachEvent("on" + name, wrapper);
			}

			return element;
		},

		stopObserving: function(element, eventName, handler) {
			element = $(element);
			var id = getEventID(element), name = getDOMEventName(eventName);

			if (!handler && eventName) {
				getWrappersForEventName(id, eventName).each(function(wrapper) {
					element.stopObserving(eventName, wrapper.handler);
				});
				return element;

			} else if (!eventName) {
				Object.keys(getCacheForID(id)).each(function(eventName) {
					element.stopObserving(eventName);
				});
				return element;
			}

			var wrapper = findWrapper(id, eventName, handler);
			if (!wrapper) return element;

			if (element.removeEventListener) {
				element.removeEventListener(name, wrapper, false);
			} else {
				element.detachEvent("on" + name, wrapper);
			}

			destroyWrapper(id, eventName, handler);

			return element;
		},

		fire: function(element, eventName, memo) {
			element = $(element);
			if (element == document && document.createEvent && !element.dispatchEvent)
				element = document.documentElement;

			var event;
			if (document.createEvent) {
				event = document.createEvent("HTMLEvents");
				event.initEvent("dataavailable", true, true);
			} else {
				event = document.createEventObject();
				event.eventType = "ondataavailable";
			}

			event.eventName = eventName;
			event.memo = memo || { };

			if (document.createEvent) {
				element.dispatchEvent(event);
			} else {
				element.fireEvent(event.eventType, event);
			}

			return Event.extend(event);
		}
	};
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
	fire:			Event.fire,
	observe:		Event.observe,
	stopObserving:	Event.stopObserving
});

Object.extend(document, {
	fire:			Element.Methods.fire.methodize(),
	observe:		Element.Methods.observe.methodize(),
	stopObserving:	Element.Methods.stopObserving.methodize()
});

//mouse:wheel
(function(){
	function wheel(event){
		var delta;
		// normalize the delta
		if(event.wheelDelta) // IE & Opera
			delta = event.wheelDelta / 120;
		else if (event.detail) // W3C
			delta =- event.detail / 3;
		if(!delta)
			return;
		var custom_event = event.element().fire('mouse:wheel',{
			delta: delta
		});
		if(custom_event.stopped){
			event.stop();
			return false;
		}
	}
	document.observe('mousewheel',wheel);
	document.observe('DOMMouseScroll',wheel);
})();

/* End Core Extensions */

//from PrototypeUI
var IframeShim = Class.create({
	initialize: function() {
		this.element = new Element('iframe',{
			style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
			src: 'javascript:void(0);',
			frameborder: 0 
		});
		$(document.body).insert(this.element);
	},
	hide: function() {
		this.element.hide();
		return this;
	},
	show: function() {
		this.element.show();
		return this;
	},
	positionUnder: function(element) {
		var element = $(element);
		var offset = element.cumulativeOffset();
		var dimensions = element.getDimensions();
		this.element.setStyle({
			left: offset[0] + 'px',
			top: offset[1] + 'px',
			width: dimensions.width + 'px',
			height: dimensions.height + 'px',
			zIndex: element.getStyle('zIndex') - 1
		}).show();
		return this;
	},
	setBounds: function(bounds) {
		for(prop in bounds)
			bounds[prop] += 'px';
		this.element.setStyle(bounds);
		return this;
	},
	destroy: function() {
		if(this.element)
			this.element.remove();
		return this;
	}
});
