X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fevent.js;h=983e9e52f331ba25d515ff2c768336ef3ea4f18f;hb=5dc6b7ce3469eaadb37a151d449e8d36571d1894;hp=a32f0339c8d9829e7b97b4f888fa9d682e8db588;hpb=f25eedf32a7555f71a246c6f491292ce876c4133;p=jquery.git diff --git a/src/event.js b/src/event.js index a32f033..983e9e5 100644 --- a/src/event.js +++ b/src/event.js @@ -37,13 +37,20 @@ jQuery.event = { // 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 eventHandle() { + handle = jQuery.data( elem, "handle" ), eventHandle; + + if ( !handle ) { + eventHandle = function() { // Handle the second event of a trigger and when // an event is called after a page has unloaded return typeof jQuery !== "undefined" && !jQuery.event.triggered ? jQuery.event.handle.apply( eventHandle.elem, arguments ) : undefined; - }); + }; + + handle = jQuery.data( elem, "handle", eventHandle ); + } + // Add elem as a property of the handle function // This is to prevent a memory leak with non-native // event in IE. @@ -605,50 +612,85 @@ jQuery.event.special.submit = { // change delegation, happens here so we have bind. if ( !jQuery.support.changeBubbles ) { -jQuery.event.special.change = { - filters: { - click: function( e ) { - var elem = e.target; +var formElems = /textarea|input|select/i; - if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) { - return trigger( "change", this, arguments ); - } +function getVal( elem ) { + var type = elem.type, val = elem.value; - return changeFilters.keyup.call( this, e ); - }, - keyup: function( e ) { - var elem = e.target, data, index = elem.selectedIndex + ""; + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; - if ( elem.nodeName.toLowerCase() === "select" ) { - data = jQuery.data( elem, "_change_data" ); - jQuery.data( elem, "_change_data", index ); + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; - if ( (elem.type === "select-multiple" || data != null) && data !== index ) { - return trigger( "change", this, arguments ); - } - } - }, - beforeactivate: function( e ) { - var elem = e.target; + } else if ( elem.nodeName.toLowerCase() === "select" ) { + val = elem.selectedIndex; + } + + return val; +} + +function testChange( e ) { + var elem = e.target, data, val; - if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) { - return trigger( "change", this, arguments ); + if ( !formElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery.data( elem, "_change_data" ); + val = getVal(elem); + + if ( val === data ) { + return; + } + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery.data( elem, "_change_data", val ); + } + + if ( elem.type !== "select" && (data != null || val) ) { + e.type = "change"; + return jQuery.event.trigger( e, arguments[1], this ); + } +} + +jQuery.event.special.change = { + filters: { + focusout: testChange, + + click: function( e ) { + var elem = e.target, type = elem.type; + + if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { + return testChange.call( this, e ); } }, - blur: function( e ) { - var elem = e.target, nodeName = elem.nodeName.toLowerCase(); - if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password"))) - && jQuery.data(elem, "_change_data") !== elem.value ) { + // Change has to be called before submit + // Keydown will be called before keypress, wich is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = elem.type; - return trigger( "change", this, arguments ); + if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + return testChange.call( this, e ); } }, - focus: function( e ) { - var elem = e.target, nodeName = elem.nodeName.toLowerCase(); - if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) { - jQuery.data( elem, "_change_data", elem.value ); + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information/focus[in] is not needed anymore + beforeactivate: function( e ) { + var elem = e.target; + + if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) { + return jQuery.data( elem, "_change_data", getVal(elem) ); } } }, @@ -656,14 +698,15 @@ jQuery.event.special.change = { for ( var type in changeFilters ) { jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] ); } - - // always want to listen for change for trigger - return false; + + return formElems.test( this.nodeName ); }, remove: function( namespaces, fn ) { for ( var type in changeFilters ) { jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] ); } + + return formElems.test( this.nodeName ); } }; @@ -680,7 +723,7 @@ function trigger( type, elem, args ) { if ( !jQuery.support.focusBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){ - event.special[ orig ] = { + jQuery.event.special[ orig ] = { setup: function() { jQuery.event.add( this, fix, ieHandler ); }, @@ -697,33 +740,12 @@ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){ } -jQuery.fn.extend({ - // TODO: make bind(), unbind() and one() DRY! - bind: function( type, data, fn, thisObject ) { - // Handle object literals - if ( typeof type === "object" ) { - for ( var key in type ) { - this.bind(key, data, type[key], fn); - } - return this; - } - - if ( jQuery.isFunction( data ) ) { - thisObject = fn; - fn = data; - data = undefined; - } - fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject ); - return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() { - jQuery.event.add( this, type, fn, data ); - }); - }, - - one: function( type, data, fn, thisObject ) { +jQuery.each(["bind", "one"], function(i, name) { + jQuery.fn[ name ] = function( type, data, fn, thisObject ) { // Handle object literals if ( typeof type === "object" ) { for ( var key in type ) { - this.one(key, data, type[key], fn); + this[ name ](key, data, type[key], fn); } return this; } @@ -734,15 +756,17 @@ jQuery.fn.extend({ data = undefined; } fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject ); - var one = jQuery.event.proxy( fn, function( event ) { - jQuery( this ).unbind( event, one ); + var handler = name == "one" ? jQuery.event.proxy( fn, function( event ) { + jQuery( this ).unbind( event, handler ); return fn.apply( this, arguments ); + }) : fn; + return type === "unload" ? this.one(type, data, handler, thisObject) : this.each(function() { + jQuery.event.add( this, type, handler, data ); }); - return this.each(function() { - jQuery.event.add( this, type, one, data ); - }); - }, + }; +}); +jQuery.fn.extend({ unbind: function( type, fn ) { // Handle object literals if ( typeof type === "object" && !type.preventDefault ) { @@ -751,12 +775,11 @@ jQuery.fn.extend({ } return this; } - + return this.each(function() { jQuery.event.remove( this, type, fn ); }); }, - trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); @@ -895,16 +918,15 @@ jQuery.each( ("blur focus load resize scroll unload click dblclick " + // Window isn't included so as not to unbind existing unload events // More info: // - http://isaacschlueter.com/2006/10/msie-memory-leaks/ -/*@cc_on -jQuery( window ).bind( 'unload', function() { - for ( var id in jQuery.cache ) { - // Skip the window - if ( id != 1 && jQuery.cache[ id ].handle ) { - // Try/Catch is to handle iframes being unloaded, see #4280 - try { - jQuery.event.remove( jQuery.cache[ id ].handle.elem ); - } catch(e) {} +if ( window.attachEvent && !window.addEventListener ) { + window.attachEvent("onunload", function() { + for ( var id in jQuery.cache ) { + if ( jQuery.cache[ id ].handle ) { + // Try/Catch is to handle iframes being unloaded, see #4280 + try { + jQuery.event.remove( jQuery.cache[ id ].handle.elem ); + } catch(e) {} + } } - } -}); -@*/ + }); +}