/**
 * @author Ryan Johnson <http://syntacticx.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 = {};
       
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._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
                                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){
                        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._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;
                        };
                }
        }
};

/* 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._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;
        }
});

