/**
 * @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;
 }
});