docs: function( elem, types, handler ) {
  		// don't do events on text and comment nodes
  		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
  			return;
  		}
  
  		var events = jQuery.data( elem, "events" ), ret, type, fn;
  
  		if ( events ) {
  			// Unbind all events for the element
  			if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
  				for ( type in events ) {
  					this.remove( elem, type + (types || "") );
  				}
  			} else {
  				// types is actually an event object here
  				if ( types.type ) {
  					handler = types.handler;
  					types = types.type;
  				}
  
  				// Handle multiple events separated by a space
  				// jQuery(...).unbind("mouseover mouseout", fn);
  				types = types.split(/\s+/);
  				var i = 0;
  				while ( (type = types[ i++ ]) ) {
  					// Namespaced event handlers
  					var namespaces = type.split(".");
  					type = namespaces.shift();
  					var all = !namespaces.length,
  						cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ),
  						namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
  						special = this.special[ type ] || {};
  
  					if ( events[ type ] ) {
  						// remove the given handler for the given type
  						if ( handler ) {
  							fn = events[ type ][ handler.guid ];
  							delete events[ type ][ handler.guid ];
  
  						// remove all handlers for the given type
  						} else {
  							for ( var handle in events[ type ] ) {
  								// Handle the removal of namespaced events
  								if ( all || namespace.test( events[ type ][ handle ].type ) ) {
  									delete events[ type ][ handle ];
  								}
  							}
  						}
  
  						if ( special.remove ) {
  							special.remove.call( elem, namespaces, fn);
  						}
  
  						// remove generic event handler if no more handlers exist
  						for ( ret in events[ type ] ) {
  							break;
  						}
  						if ( !ret ) {
  							if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
  								if ( elem.removeEventListener ) {
  									elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
  								} else if ( elem.detachEvent ) {
  									elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
  								}
  							}
  							ret = null;
  							delete events[ type ];
  						}
  					}
  				}
  			}
  
  			// Remove the expando if it's no longer used
  			for ( ret in events ) {
  				break;
  			}
  			if ( !ret ) {
  				var handle = jQuery.data( elem, "handle" );
  				if ( handle ) {
  					handle.elem = null;
  				}
  				jQuery.removeData( elem, "events" );
  				jQuery.removeData( elem, "handle" );
  			}
  		}
  	}