/* * 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(element, type, handler, data) { // For whatever reason, IE has trouble passing the window object // around, causing it to be cloned in the process if ( jQuery.browser.msie && element.setInterval != undefined ) element = 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; } // Namespaced event handlers var parts = type.split("."); type = parts[0]; handler.type = parts[1]; // Init the element's event structure var events = jQuery.data(element, "events") || jQuery.data(element, "events", {}); var handle = jQuery.data(element, "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(element, arguments); return val; }); // Get the current list of functions bound to this event var handlers = events[type]; // Init the event handler queue if (!handlers) { handlers = events[type] = {}; // And bind the global event handler to the element if (element.addEventListener) element.addEventListener(type, handle, false); else element.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 this.global[type] = true; }, guid: 1, global: {}, // Detach an event or set of events from an element remove: function(element, type, handler) { var events = jQuery.data(element, "events"), ret, index; // Namespaced event handlers if ( typeof type == "string" ) { var parts = type.split("."); type = parts[0]; } if ( events ) { // type is actually an event object here if ( type && type.type ) { handler = type.handler; type = type.type; } if ( !type ) { for ( type in events ) this.remove( element, type ); } else 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 (element.removeEventListener) element.removeEventListener(type, jQuery.data(element, "handle"), false); else element.detachEvent("on" + type, jQuery.data(element, "handle")); ret = null; delete events[type]; } } // Remove the expando if it's no longer used for ( ret in events ) break; if ( !ret ) { jQuery.removeData( element, "events" ); jQuery.removeData( element, "handle" ); } } }, trigger: function(type, data, element, donative, extra) { // Clone the incoming data, if any data = jQuery.makeArray(data || []); // Handle a global trigger if ( !element ) { // 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 { var val, ret, fn = jQuery.isFunction( element[ type ] || null ), // Check to see if we need to provide a fake event, or not evt = !data[0] || !data[0].preventDefault; // Pass along a fake event if ( evt ) data.unshift( this.fix({ type: type, target: element }) ); // Trigger the event if ( jQuery.isFunction( jQuery.data(element, "handle") ) ) val = jQuery.data(element, "handle").apply( element, data ); // Handle triggering native .onfoo handlers if ( !fn && element["on"+type] && element["on"+type].apply( element, data ) === false ) val = false; // Extra functions don't get the custom event object if ( evt ) data.shift(); // Handle triggering of extra function if ( extra && extra.apply( element, data ) === false ) val = false; // Trigger the native events (except for clicks on links) if ( fn && donative !== false && val !== false && !(jQuery.nodeName(element, 'a') && type == "click") ) { this.triggered = true; element[ type ](); } 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 c = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 ); args.unshift( event ); for ( var j in c ) { // Pass in a reference to the handler function itself // So that we can later remove it args[0].handler = c[j]; args[0].data = c[j].data; // Filter the functions by class if ( !parts[1] || c[j].type == parts[1] ) { var tmp = c[j].apply( this, args ); if ( val !== false ) val = tmp; if ( tmp === 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) { // store a copy of the original event object // and clone to set read-only properties var originalEvent = event; event = jQuery.extend({}, originalEvent); // 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.srcElement ) event.target = event.srcElement; // check if target is a textnode (safari) if (jQuery.browser.safari && 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 e = document.documentElement, b = document.body; event.pageX = event.clientX + (e && e.scrollLeft || b.scrollLeft || 0); event.pageY = event.clientY + (e && e.scrollTop || b.scrollTop || 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; } }; 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 ); }, toggle: function() { // Save reference to arguments for access in closure var a = arguments; return this.click(function(e) { // Figure out which function to execute this.lastToggle = 0 == this.lastToggle ? 1 : 0; // Make sure that clicks stop e.preventDefault(); // and execute the function return a[this.lastToggle].apply( this, [e] ) || false; }); }, hover: function(f,g) { // A private function for handling mouse 'hovering' function handleHover(e) { // Check if mouse(over|out) are still within the same parent element var p = e.relatedTarget; // Traverse up the tree while ( p && p != this ) try { p = p.parentNode; } catch(e) { p = this; }; // If we actually just moused on to a sub-element, ignore it if ( p == this ) return false; // Execute the right function return (e.type == "mouseover" ? f : g).apply(this, [e]); } // Bind the function to the two event listeners return this.mouseover(handleHover).mouseout(handleHover); }, ready: function(f) { // Attach the listeners bindReady(); // If the DOM is already ready if ( jQuery.isReady ) // Execute the function immediately f.apply( document, [jQuery] ); // Otherwise, remember the function for later else // Add the function to the wait list jQuery.readyList.push( function() { return f.apply(this, [jQuery]); } ); return this; } }); jQuery.extend({ /* * All the code that makes DOM Ready work nicely. */ 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; } // Remove event listener to avoid memory leak if ( jQuery.browser.mozilla || jQuery.browser.opera ) document.removeEventListener( "DOMContentLoaded", jQuery.ready, false ); // Remove script element used by IE hack if( !window.frames.length ) // don't remove if frames are present (#1187) jQuery(window).load(function(){ jQuery("#__ie_init").remove(); }); } } }); 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,o){ // Handle event binding jQuery.fn[o] = function(f){ return f ? this.bind(o, f) : this.trigger(o); }; }); var readyBound = false; function bindReady(){ if ( readyBound ) return; readyBound = true; // If Mozilla is used if ( jQuery.browser.mozilla || jQuery.browser.opera ) // Use the handy event callback document.addEventListener( "DOMContentLoaded", jQuery.ready, false ); // If IE is used, use the excellent hack by Matthias Miller // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited else if ( jQuery.browser.msie ) { // Only works if you document.write() it document.write("<\/script>"); // Use the defer script hack var script = document.getElementById("__ie_init"); // script does not exist if jQuery is loaded dynamically if ( script ) script.onreadystatechange = function() { if ( this.readyState != "complete" ) return; jQuery.ready(); }; // Clear from memory script = null; // If Safari is used } else if ( jQuery.browser.safari ) // Continually check to see if the document.readyState is valid jQuery.safariTimer = setInterval(function(){ // loaded and complete are both valid states if ( document.readyState == "loaded" || document.readyState == "complete" ) { // If either one are found, remove the timer clearInterval( jQuery.safariTimer ); jQuery.safariTimer = null; // and execute any waiting functions jQuery.ready(); } }, 10); // A fallback to window.onload, that will always work jQuery.event.add( window, "load", jQuery.ready ); }