/* * A number of helper functions used for managing events. * Many of the ideas behind this code orignated from * Dean Edwards' addEvent library. */ jQuery.event = { // Bind an event to an element // Original by Dean Edwards add: function(elem, types, handler, data) { if ( elem.nodeType == 3 || elem.nodeType == 8 ) return; // For whatever reason, IE has trouble passing the window object // around, causing it to be cloned in the process if ( jQuery.browser.msie && elem.setInterval != undefined ) elem = window; // Make sure that the function being executed has a unique ID if ( !handler.guid ) handler.guid = this.guid++; // if data is passed, bind to handler if( data != undefined ) { // Create temporary function pointer to original handler var fn = handler; // Create unique handler function, wrapped around original handler handler = function() { // Pass arguments and context to original handler return fn.apply(this, arguments); }; // Store data in unique handler handler.data = data; // Set the guid of unique handler to the same of original handler, so it can be removed handler.guid = fn.guid; } // Init the element's event structure var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}), handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){ // returned undefined or false var val; // Handle the second event of a trigger and when // an event is called after a page has unloaded if ( typeof jQuery == "undefined" || jQuery.event.triggered ) return val; val = jQuery.event.handle.apply(elem, arguments); return val; }); // Handle multiple events seperated by a space // jQuery(...).bind("mouseover mouseout", fn); jQuery.each(types.split(/\s+/), function(index, type) { // Namespaced event handlers var parts = type.split("."); type = parts[0]; handler.type = parts[1]; // Get the current list of functions bound to this event var handlers = events[type]; // Init the event handler queue if (!handlers) { handlers = events[type] = {}; // Check for a special event handler // Only use addEventListener/attachEvent if the special // events handler returns false if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) { // Bind the global event handler to the element if (elem.addEventListener) elem.addEventListener(type, handle, false); else if (elem.attachEvent) elem.attachEvent("on" + type, handle); } } // Add the function to the element's handler list handlers[handler.guid] = handler; // Keep track of which events have been used, for global triggering jQuery.event.global[type] = true; }); }, guid: 1, global: {}, // Detach an event or set of events from an element remove: function(elem, types, handler) { // don't do events on text and comment nodes if ( elem.nodeType == 3 || elem.nodeType == 8 ) return; var events = jQuery.data(elem, "events"), ret, index; if ( events ) { // Unbind all events for the element if ( types == undefined ) for ( var type in events ) this.remove( elem, type ); else { // types is actually an event object here if ( types.type ) { handler = types.handler; types = types.type; } // Handle multiple events seperated by a space // jQuery(...).unbind("mouseover mouseout", fn); jQuery.each(types.split(/\s+/), function(index, type){ // Namespaced event handlers var parts = type.split("."); type = parts[0]; if ( events[type] ) { // remove the given handler for the given type if ( handler ) delete events[type][handler.guid]; // remove all handlers for the given type else for ( handler in events[type] ) // Handle the removal of namespaced events if ( !parts[1] || events[type][handler].type == parts[1] ) delete events[type][handler]; // remove generic event handler if no more handlers exist for ( ret in events[type] ) break; if ( !ret ) { if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) { if (elem.removeEventListener) elem.removeEventListener(type, jQuery.data(elem, "handle"), false); else if (elem.detachEvent) elem.detachEvent("on" + type, jQuery.data(elem, "handle")); } ret = null; delete events[type]; } } }); } // Remove the expando if it's no longer used for ( ret in events ) break; if ( !ret ) { jQuery.removeData( elem, "events" ); jQuery.removeData( elem, "handle" ); } } }, trigger: function(type, data, elem, donative, extra) { // Clone the incoming data, if any data = jQuery.makeArray(data || []); // Handle a global trigger if ( !elem ) { // Only trigger if we've ever bound an event for it if ( this.global[type] ) jQuery("*").add([window, document]).trigger(type, data); // Handle triggering a single element } else { // don't do events on text and comment nodes if ( elem.nodeType == 3 || elem.nodeType == 8 ) return undefined; var val, ret, fn = jQuery.isFunction( elem[ type ] || null ), // Check to see if we need to provide a fake event, or not event = !data[0] || !data[0].preventDefault; // Pass along a fake event if ( event ) data.unshift( this.fix({ type: type, target: elem }) ); // Enforce the right trigger type data[0].type = type; // Trigger the event if ( jQuery.isFunction( jQuery.data(elem, "handle") ) ) val = jQuery.data(elem, "handle").apply( elem, data ); // Handle triggering native .onfoo handlers if ( !fn && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) val = false; // Extra functions don't get the custom event object if ( event ) data.shift(); // Handle triggering of extra function if ( extra && jQuery.isFunction( extra ) ) { // call the extra function and tack the current return value on the end for possible inspection var ret = extra.apply( elem, data.concat( val ) ); // if anything is returned, give it precedence and have it overwrite the previous value if (ret !== undefined) val = ret; } // Trigger the native events (except for clicks on links) if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click") ) { this.triggered = true; try { elem[ type ](); // prevent IE from throwing an error for some hidden elements } catch (e) {} } this.triggered = false; } return val; }, handle: function(event) { // returned undefined or false var val; // Empty object is for triggered events with no data event = jQuery.event.fix( event || window.event || {} ); // Namespaced event handlers var parts = event.type.split("."); event.type = parts[0]; var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 ); args.unshift( event ); for ( var j in handlers ) { var handler = handlers[j]; // Pass in a reference to the handler function itself // So that we can later remove it args[0].handler = handler; args[0].data = handler.data; // Filter the functions by class if ( !parts[1] || handler.type == parts[1] ) { var ret = handler.apply( this, args ); if ( val !== false ) val = ret; if ( ret === false ) { event.preventDefault(); event.stopPropagation(); } } } // Clean up added properties in IE to prevent memory leak if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null; return val; }, fix: function(event) { // Short-circuit if the event has already been fixed by jQuery.event.fix if ( event[ expando ] ) return event; // store a copy of the original event object // and clone to set read-only properties var originalEvent = event; event = jQuery.extend({}, originalEvent); // Mark the event as fixed by jQuery.event.fix event[ expando ] = true; // add preventDefault and stopPropagation since // they will not work on the clone event.preventDefault = function() { // if preventDefault exists run it on the original event if (originalEvent.preventDefault) originalEvent.preventDefault(); // otherwise set the returnValue property of the original event to false (IE) originalEvent.returnValue = false; }; event.stopPropagation = function() { // if stopPropagation exists run it on the original event if (originalEvent.stopPropagation) originalEvent.stopPropagation(); // otherwise set the cancelBubble property of the original event to true (IE) originalEvent.cancelBubble = true; }; // Fix target property, if necessary if ( !event.target ) event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either // check if target is a textnode (safari) if ( event.target.nodeType == 3 ) event.target = originalEvent.target.parentNode; // Add relatedTarget, if necessary if ( !event.relatedTarget && event.fromElement ) event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement; // Calculate pageX/Y if missing and clientX/Y available if ( event.pageX == null && event.clientX != null ) { var doc = document.documentElement, body = document.body; event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0); event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0); } // Add which for key events if ( !event.which && (event.charCode || event.keyCode) ) event.which = event.charCode || event.keyCode; // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) if ( !event.metaKey && event.ctrlKey ) event.metaKey = event.ctrlKey; // Add which for click: 1 == left; 2 == middle; 3 == right // Note: button is not normalized, so don't use it if ( !event.which && event.button ) event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); return event; }, special: { ready: { setup: function() { // Make sure the ready event is setup bindReady(); return; }, teardown: function() { return; } }, mouseenter: { setup: function() { if ( jQuery.browser.msie ) return false; jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler); return true; }, teardown: function() { if ( jQuery.browser.msie ) return false; jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler); return true; }, handler: function(event) { // If we actually just moused on to a sub-element, ignore it if ( withinElement(event, this) ) return true; // Execute the right handlers by setting the event type to mouseenter arguments[0].type = "mouseenter"; return jQuery.event.handle.apply(this, arguments); } }, mouseleave: { setup: function() { if ( jQuery.browser.msie ) return false; jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler); return true; }, teardown: function() { if ( jQuery.browser.msie ) return false; jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler); return true; }, handler: function(event) { // If we actually just moused on to a sub-element, ignore it if ( withinElement(event, this) ) return true; // Execute the right handlers by setting the event type to mouseleave arguments[0].type = "mouseleave"; return jQuery.event.handle.apply(this, arguments); } } } }; jQuery.fn.extend({ bind: function( type, data, fn ) { return type == "unload" ? this.one(type, data, fn) : this.each(function(){ jQuery.event.add( this, type, fn || data, fn && data ); }); }, one: function( type, data, fn ) { return this.each(function(){ jQuery.event.add( this, type, function(event) { jQuery(this).unbind(event); return (fn || data).apply( this, arguments); }, fn && data); }); }, unbind: function( type, fn ) { return this.each(function(){ jQuery.event.remove( this, type, fn ); }); }, trigger: function( type, data, fn ) { return this.each(function(){ jQuery.event.trigger( type, data, this, true, fn ); }); }, triggerHandler: function( type, data, fn ) { if ( this[0] ) return jQuery.event.trigger( type, data, this[0], false, fn ); return undefined; }, toggle: function() { // Save reference to arguments for access in closure var args = arguments; return this.click(function(event) { // Figure out which function to execute this.lastToggle = 0 == this.lastToggle ? 1 : 0; // Make sure that clicks stop event.preventDefault(); // and execute the function return args[this.lastToggle].apply( this, arguments ) || false; }); }, hover: function(fnOver, fnOut) { return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut); }, ready: function(fn) { // Attach the listeners bindReady(); // If the DOM is already ready if ( jQuery.isReady ) // Execute the function immediately fn.call( document, jQuery ); // Otherwise, remember the function for later else // Add the function to the wait list jQuery.readyList.push( function() { return fn.call(this, jQuery); } ); return this; } }); jQuery.extend({ isReady: false, readyList: [], // Handle when the DOM is ready ready: function() { // Make sure that the DOM is not already loaded if ( !jQuery.isReady ) { // Remember that the DOM is ready jQuery.isReady = true; // If there are functions bound, to execute if ( jQuery.readyList ) { // Execute all of them jQuery.each( jQuery.readyList, function(){ this.apply( document ); }); // Reset the list of functions jQuery.readyList = null; } // Trigger any bound ready events $(document).triggerHandler("ready"); } } }); var readyBound = false; function bindReady(){ if ( readyBound ) return; readyBound = true; // Mozilla, Opera and webkit nightlies currently support this event if ( document.addEventListener ) // Use the handy event callback document.addEventListener( "DOMContentLoaded", jQuery.ready, false ); // If Safari or IE is used // Continually check to see if the document is ready if (jQuery.browser.msie || jQuery.browser.safari ) (function(){ try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ if ( jQuery.browser.msie || document.readyState != "loaded" && document.readyState != "complete" ) document.documentElement.doScroll("left"); } catch( error ) { return setTimeout( arguments.callee, 0 ); } // and execute any waiting functions jQuery.ready(); })(); // A fallback to window.onload, that will always work jQuery.event.add( window, "load", jQuery.ready ); } jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," + "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + "submit,keydown,keypress,keyup,error").split(","), function(i, name){ // Handle event binding jQuery.fn[name] = function(fn){ return fn ? this.bind(name, fn) : this.trigger(name); }; }); // Checks if an event happened on an element within another element // Used in jQuery.event.special.mouseenter and mouseleave handlers var withinElement = function(event, elem) { // Check if mouse(over|out) are still within the same parent element var parent = event.relatedTarget; // Traverse up the tree while ( parent && parent != elem ) try { parent = parent.parentNode } catch(error) { parent = elem; }; // Return true if we actually just moused on to a sub-element return parent == elem; }; // Prevent memory leaks in IE // And prevent errors on refresh with events like mouseover in other browsers // Window isn't included so as not to unbind existing unload events jQuery(window).bind("unload", function() { jQuery("*").add(document).unbind(); });