X-Git-Url: http://git.asbjorn.it/?a=blobdiff_plain;f=src%2Fevent.js;h=73a6eacc8afc64b237a913d71cbc2cefeede7a98;hb=c03a2801556d42d60fed1a5b4ce5d25362ccd9f5;hp=e9e1beec249ca064c3321b03b28e6f2c397f4114;hpb=aa9236e5ea8c91fbf60d440872e4946a0452f852;p=jquery.git diff --git a/src/event.js b/src/event.js index e9e1bee..73a6eac 100644 --- a/src/event.js +++ b/src/event.js @@ -13,7 +13,7 @@ jQuery.event = { // 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 ) + if ( elem.setInterval && elem != window ) elem = window; // Make sure that the function being executed has a unique ID @@ -21,7 +21,7 @@ jQuery.event = { handler.guid = this.guid++; // if data is passed, bind to handler - if( data != undefined ) { + if ( data !== undefined ) { // Create temporary function pointer to original handler var fn = handler; @@ -40,8 +40,9 @@ jQuery.event = { handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){ // 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 jQuery.event.handle.apply(arguments.callee.elem, arguments); + return typeof jQuery !== "undefined" && !jQuery.event.triggered ? + jQuery.event.handle.apply(arguments.callee.elem, arguments) : + undefined; }); // Add elem as a property of the handle function // This is to prevent a memory leak with non-native @@ -52,12 +53,15 @@ jQuery.event = { // 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]; + var namespaces = type.split("."); + type = namespaces.shift(); + handler.type = namespaces.slice().sort().join("."); // Get the current list of functions bound to this event var handlers = events[type]; + + if ( jQuery.event.specialAll[type] ) + jQuery.event.specialAll[type].setup.call(elem, data, namespaces); // Init the event handler queue if (!handlers) { @@ -66,7 +70,7 @@ jQuery.event = { // 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,data) === false ) { + if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) { // Bind the global event handler to the element if (elem.addEventListener) elem.addEventListener(type, handle, false); @@ -99,7 +103,7 @@ jQuery.event = { if ( events ) { // Unbind all events for the element - if ( types == undefined || (typeof types == "string" && types.charAt(0) == ".") ) + if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") ) for ( var type in events ) this.remove( elem, type + (types || "") ); else { @@ -113,8 +117,9 @@ jQuery.event = { // jQuery(...).unbind("mouseover mouseout", fn); jQuery.each(types.split(/\s+/), function(index, type){ // Namespaced event handlers - var parts = type.split("."); - type = parts[0]; + var namespaces = type.split("."); + type = namespaces.shift(); + var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"); if ( events[type] ) { // remove the given handler for the given type @@ -125,13 +130,16 @@ jQuery.event = { else for ( handler in events[type] ) // Handle the removal of namespaced events - if ( !parts[1] || events[type][handler].type == parts[1] ) + if ( namespace.test(events[type][handler].type) ) delete events[type][handler]; + + if ( jQuery.event.specialAll[type] ) + jQuery.event.specialAll[type].teardown.call(elem, namespaces); // 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 ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) { if (elem.removeEventListener) elem.removeEventListener(type, jQuery.data(elem, "handle"), false); else if (elem.detachEvent) @@ -155,7 +163,7 @@ jQuery.event = { } }, - trigger: function(type, data, elem, donative, extra) { + trigger: function(type, data, elem, donative, extra, dohandlers) { // Clone the incoming data, if any data = jQuery.makeArray(data); @@ -201,14 +209,22 @@ jQuery.event = { if ( exclusive ) data[0].exclusive = true; - // Trigger the event, it is assumed that "handle" is a function - var handle = jQuery.data(elem, "handle"); - if ( handle ) - val = handle.apply( elem, data ); + if ( dohandlers !== false ) { + // Trigger the event, it is assumed that "handle" is a function + var handle = jQuery.data(elem, "handle"); + if ( handle ) + val = handle.apply( elem, data ); - // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links) - if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) - val = false; + // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links) + if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) + val = false; + } + + if ( donative !== false && val !== false ) { + var parent = elem.parentNode || elem.ownerDocument; + if ( parent ) + jQuery.event.trigger(type, data, parent, donative); + } // Extra functions don't get the custom event object if ( event ) @@ -219,7 +235,7 @@ jQuery.event = { // call the extra function and tack the current return value on the end for possible inspection ret = extra.apply( elem, val == null ? data : data.concat( val ) ); // if anything is returned, give it precedence and have it overwrite the previous value - if (ret !== undefined) + if ( ret !== undefined ) val = ret; } @@ -240,16 +256,18 @@ jQuery.event = { handle: function(event) { // returned undefined or false - var val, ret, namespace, all, handlers; + var val, ret, all, handlers; event = arguments[0] = jQuery.event.fix( event || window.event ); // Namespaced event handlers - namespace = event.type.split("."); - event.type = namespace[0]; - namespace = namespace[1]; + var namespaces = event.type.split("."); + event.type = namespaces.shift(); + // Cache this now, all = true means, any handler - all = !namespace && !event.exclusive; + all = !namespaces.length && !event.exclusive; + + var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"); handlers = ( jQuery.data(this, "events") || {} )[event.type]; @@ -257,7 +275,7 @@ jQuery.event = { var handler = handlers[j]; // Filter the functions by class - if ( all || handler.type == namespace ) { + if ( all || namespace.test(handler.type) ) { // Pass in a reference to the handler function itself // So that we can later remove it event.handler = handler; @@ -371,6 +389,27 @@ jQuery.event = { setup: bindReady, teardown: function() {} } + }, + + specialAll: { + live: { + setup: function( selector, namespaces ){ + jQuery.event.add( this, namespaces[0], liveHandler ); + }, + teardown: function( namespaces ){ + if ( namespaces.length ) { + var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)"); + + jQuery.each( (jQuery.data(this, "events").live || {}), function(){ + if ( name.test(this.type) ) + remove++; + }); + + if ( remove <= 1 ) + jQuery.event.remove( this, namespaces[0], liveHandler ); + } + } + } } }; @@ -379,39 +418,37 @@ function stopImmediatePropagation(){ this.stopPropagation(); } -if ( !jQuery.browser.msie ){ - // Checks if an event happened on an element within another element - // Used in jQuery.event.special.mouseenter and mouseleave handlers - var withinElement = function(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(e) { parent = this; } - - if( parent != this ){ - // set the correct event type - event.type = event.data; - // handle event if we actually just moused on to a non sub-element - jQuery.event.handle.apply( this, arguments ); - } - }; +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function(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(e) { parent = this; } - jQuery.each({ - mouseover: 'mouseenter', - mouseout: 'mouseleave' - }, function( orig, fix ){ - jQuery.event.special[ fix ] = { - setup: function(){ - jQuery.event.add( this, orig, withinElement, fix ); - }, - teardown: function(){ - jQuery.event.remove( this, orig, withinElement ); - } - }; - }); -} + if( parent != this ){ + // set the correct event type + event.type = event.data; + // handle event if we actually just moused on to a non sub-element + jQuery.event.handle.apply( this, arguments ); + } +}; + +jQuery.each({ + mouseover: 'mouseenter', + mouseout: 'mouseleave' +}, function( orig, fix ){ + jQuery.event.special[ fix ] = { + setup: function(){ + jQuery.event.add( this, orig, withinElement, fix ); + }, + teardown: function(){ + jQuery.event.remove( this, orig, withinElement ); + } + }; +}); jQuery.fn.extend({ bind: function( type, data, fn ) { @@ -467,7 +504,7 @@ jQuery.fn.extend({ }, hover: function(fnOver, fnOut) { - return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut); + return this.mouseenter(fnOver).mouseleave(fnOut); }, ready: function(fn) { @@ -485,9 +522,34 @@ jQuery.fn.extend({ jQuery.readyList.push( function() { return fn.call(this, jQuery); } ); return this; + }, + + live: function( type, fn ){ + jQuery(document).bind( liveConvert(type, this.selector), this.selector, fn ); + return this; + }, + + die: function( type, fn ){ + jQuery(document).unbind( liveConvert(type, this.selector), fn ); + return this; } }); +function liveHandler( event ){ + var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"); + jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){ + if ( check.test(fn.type) ) { + var elem = jQuery(event.target).closest(fn.data)[0]; + if ( elem ) + jQuery.event.trigger( event.type, fn.data, elem, false, fn, false ); + } + }); +} + +function liveConvert(type, selector){ + return ["live", type, selector.replace(/\./g, "_")].join("."); +} + jQuery.extend({ isReady: false, readyList: [], @@ -521,53 +583,39 @@ function bindReady(){ if ( readyBound ) return; readyBound = true; - // Mozilla, Opera (see further below for it) and webkit nightlies currently support this event - if ( document.addEventListener && !jQuery.browser.opera) + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { // Use the handy event callback - document.addEventListener( "DOMContentLoaded", jQuery.ready, false ); - - // If IE is used and is not in a frame - // Continually check to see if the document is ready - if ( jQuery.browser.msie && window == top ) (function(){ - if (jQuery.isReady) return; - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch( error ) { - setTimeout( arguments.callee, 0 ); - return; - } - // and execute any waiting functions - jQuery.ready(); - })(); - - if ( jQuery.browser.opera ) - document.addEventListener( "DOMContentLoaded", function () { - if (jQuery.isReady) return; - for (var i = 0; i < document.styleSheets.length; i++) - if (document.styleSheets[i].disabled) { - setTimeout( arguments.callee, 0 ); - return; - } - // and execute any waiting functions + document.addEventListener( "DOMContentLoaded", function(){ + document.removeEventListener( "DOMContentLoaded", arguments.callee, false ); jQuery.ready(); - }, false); - - if ( jQuery.browser.safari ) { - var numStyles; - (function(){ - if (jQuery.isReady) return; - if ( document.readyState != "loaded" && document.readyState != "complete" ) { - setTimeout( arguments.callee, 0 ); - return; + }, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", function(){ + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", arguments.callee ); + jQuery.ready(); } - if ( numStyles === undefined ) - numStyles = jQuery("style, link[rel=stylesheet]").length; - if ( document.styleSheets.length != numStyles ) { + }); + + // If IE and not an iframe + // continually check to see if the document is ready + if ( document.documentElement.doScroll && !window.frameElement ) (function(){ + if ( jQuery.isReady ) return; + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch( error ) { setTimeout( arguments.callee, 0 ); return; } + // and execute any waiting functions jQuery.ready(); })(); @@ -578,8 +626,8 @@ function bindReady(){ } 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){ + "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," + + "change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){ // Handle event binding jQuery.fn[name] = function(fn){