Now, if anything is returned from the extra function it will overwrite the return value of the event handlers. This should only effect custom events unless someone had an extra function that returned a value other than false which would have been ignored before.
464 lines
13 KiB
JavaScript
464 lines
13 KiB
JavaScript
/*
|
|
* 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") || 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
|
|
event = !data[0] || !data[0].preventDefault;
|
|
|
|
// Pass along a fake event
|
|
if ( event )
|
|
data.unshift( this.fix({ type: type, target: element }) );
|
|
|
|
// Enforce the right trigger type
|
|
data[0].type = type;
|
|
|
|
// 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 ( event )
|
|
data.shift();
|
|
|
|
// Handle triggering of extra function
|
|
if ( extra ) {
|
|
// call the extra function and tack the current return value on the end for possible inspection
|
|
var ret = extra.apply( element, 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(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 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) {
|
|
// 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.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.clientLeft || 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 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) {
|
|
|
|
// A private function for handling mouse 'hovering'
|
|
function handleHover(event) {
|
|
// Check if mouse(over|out) are still within the same parent element
|
|
var parent = event.relatedTarget;
|
|
|
|
// Traverse up the tree
|
|
while ( parent && parent != this ) try { parent = parent.parentNode; } catch(error) { parent = this; };
|
|
|
|
// If we actually just moused on to a sub-element, ignore it
|
|
if ( parent == this )
|
|
return true;
|
|
|
|
// Execute the right function
|
|
return (event.type == "mouseover" ? fnOver : fnOut).apply(this, [event]);
|
|
}
|
|
|
|
// Bind the function to the two event listeners
|
|
return this.mouseover(handleHover).mouseout(handleHover);
|
|
},
|
|
|
|
ready: function(fn) {
|
|
// Attach the listeners
|
|
bindReady();
|
|
|
|
// If the DOM is already ready
|
|
if ( jQuery.isReady )
|
|
// Execute the function immediately
|
|
fn.apply( document, [jQuery] );
|
|
|
|
// Otherwise, remember the function for later
|
|
else
|
|
// Add the function to the wait list
|
|
jQuery.readyList.push( function() { return fn.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 ( document.removeEventListener )
|
|
document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
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);
|
|
};
|
|
});
|
|
|
|
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 );
|
|
}
|
|
|
|
// Prevent memory leaks in IE
|
|
// And prevent errors on refresh with events like mouseover
|
|
// Window isn't included so as not to unbind existing unload events
|
|
jQuery(window).bind("unload", function() {
|
|
jQuery("*").add(document).unbind();
|
|
});
|