// 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.
// 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 ( !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 );
+ }
+}
- if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
- return trigger( "change", this, arguments );
+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" ) {
+ jQuery.data( elem, "_change_data", getVal(elem) );
}
}
},
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 );
}
};
}
// Create "bubbling" focus and blur events
-if ( !jQuery.support.focusBubbles ) {
+if ( document.addEventListener ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ this.addEventListener( orig, handler, true );
+ },
+ teardown: function() {
+ this.removeEventListener( orig, handler, true );
+ }
+ };
-jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
- event.special[ orig ] = {
- setup: function() {
- jQuery.event.add( this, fix, ieHandler );
- },
- teardown: function() {
- jQuery.event.remove( this, fix, ieHandler );
+ function handler( e ) {
+ e = jQuery.event.fix( e );
+ e.type = fix;
+ return jQuery.event.handle.call( this, e );
}
- };
-
- function ieHandler() {
- arguments[0].type = orig;
- return jQuery.event.handle.apply(this, arguments);
- }
-});
-
+ });
}
-jQuery.fn.extend({
- // TODO: make bind(), unbind() and one() DRY!
- bind: 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.bind(key, data, type[key], fn);
+ this[ name ](key, data, type[key], fn);
}
return this;
}
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 ) {
- // Handle object literals
- if ( typeof type === "object" ) {
- for ( var key in type ) {
- this.one(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 );
- 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 ) {
}
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 );
jQuery.fn[ name ] = function( fn ) {
return fn ? this.bind( name, fn ) : this.trigger( name );
};
+
+ if ( jQuery.fnAttr ) {
+ jQuery.fnAttr[ name ] = true;
+ }
});
// Prevent memory leaks in IE
// 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) {}
+ }
}
- }
-});
-@*/
+ });
+}