mirror of
https://github.com/jquery/jquery.git
synced 2026-01-23 09:47:54 -05:00
2.0: Rewrite data.js (Incl. event, manipulation, tests)
This commit is contained in:
457
src/data.js
457
src/data.js
@@ -1,237 +1,201 @@
|
||||
var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
|
||||
var data_user, data_priv,
|
||||
rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
|
||||
rmultiDash = /([A-Z])/g;
|
||||
|
||||
function internalData( elem, name, data, pvt ) {
|
||||
if ( !jQuery.acceptData( elem ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var thisCache, ret,
|
||||
internalKey = jQuery.expando,
|
||||
getByName = typeof name === "string",
|
||||
|
||||
// We have to handle DOM nodes and JS objects differently because IE6-7
|
||||
// can't GC object references properly across the DOM-JS boundary
|
||||
isNode = elem.nodeType,
|
||||
|
||||
// Only DOM nodes need the global jQuery cache; JS object data is
|
||||
// attached directly to the object so GC can occur automatically
|
||||
cache = isNode ? jQuery.cache : elem,
|
||||
|
||||
// Only defining an ID for JS objects if its cache already exists allows
|
||||
// the code to shortcut on the same path as a DOM node with no cache
|
||||
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
|
||||
|
||||
// Avoid doing any more work than we need to when trying to get data on an
|
||||
// object that has no data at all
|
||||
if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !id ) {
|
||||
// Only DOM nodes need a new unique ID for each element since their data
|
||||
// ends up in the global cache
|
||||
if ( isNode ) {
|
||||
elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
|
||||
} else {
|
||||
id = internalKey;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !cache[ id ] ) {
|
||||
cache[ id ] = {};
|
||||
|
||||
// Avoids exposing jQuery metadata on plain JS objects when the object
|
||||
// is serialized using JSON.stringify
|
||||
if ( !isNode ) {
|
||||
cache[ id ].toJSON = jQuery.noop;
|
||||
}
|
||||
}
|
||||
|
||||
// An object can be passed to jQuery.data instead of a key/value pair; this gets
|
||||
// shallow copied over onto the existing cache
|
||||
if ( typeof name === "object" || typeof name === "function" ) {
|
||||
if ( pvt ) {
|
||||
cache[ id ] = jQuery.extend( cache[ id ], name );
|
||||
} else {
|
||||
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
|
||||
}
|
||||
}
|
||||
|
||||
thisCache = cache[ id ];
|
||||
|
||||
// jQuery data() is stored in a separate object inside the object's internal data
|
||||
// cache in order to avoid key collisions between internal data and user-defined
|
||||
// data.
|
||||
if ( !pvt ) {
|
||||
if ( !thisCache.data ) {
|
||||
thisCache.data = {};
|
||||
}
|
||||
|
||||
thisCache = thisCache.data;
|
||||
}
|
||||
|
||||
if ( data !== undefined ) {
|
||||
thisCache[ jQuery.camelCase( name ) ] = data;
|
||||
}
|
||||
|
||||
// Check for both converted-to-camel and non-converted data property names
|
||||
// If a data property was specified
|
||||
if ( getByName ) {
|
||||
|
||||
// First Try to find as-is property data
|
||||
ret = thisCache[ name ];
|
||||
|
||||
// Test for null|undefined property data
|
||||
if ( ret == null ) {
|
||||
|
||||
// Try to find the camelCased property
|
||||
ret = thisCache[ jQuery.camelCase( name ) ];
|
||||
}
|
||||
} else {
|
||||
ret = thisCache;
|
||||
}
|
||||
|
||||
return ret;
|
||||
function Data() {
|
||||
// Nodes|Objects
|
||||
this.owners = [];
|
||||
// Data objects
|
||||
this.cache = [];
|
||||
}
|
||||
|
||||
function internalRemoveData( elem, name, pvt ) {
|
||||
if ( !jQuery.acceptData( elem ) ) {
|
||||
return;
|
||||
}
|
||||
Data.index = function( array, node ) {
|
||||
return array.indexOf( node );
|
||||
};
|
||||
|
||||
var thisCache, i, l,
|
||||
|
||||
isNode = elem.nodeType,
|
||||
Data.prototype = {
|
||||
add: function( owner ) {
|
||||
this.owners.push( owner );
|
||||
return (this.cache[ this.owners.length - 1 ] = {});
|
||||
},
|
||||
set: function( owner, data, value ) {
|
||||
var prop,
|
||||
index = Data.index( this.owners, owner );
|
||||
|
||||
// See jQuery.data for more information
|
||||
cache = isNode ? jQuery.cache : elem,
|
||||
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
|
||||
// If there is no entry for this "owner", create one inline
|
||||
// and set the index as though an owner entry had always existed
|
||||
if ( index === -1 ) {
|
||||
this.add( owner );
|
||||
index = this.owners.length - 1;
|
||||
}
|
||||
// Handle: [ owner, key, value ] args
|
||||
if ( typeof data === "string" ) {
|
||||
this.cache[ index ][ data ] = value;
|
||||
|
||||
// If there is already no cache entry for this object, there is no
|
||||
// purpose in continuing
|
||||
if ( !cache[ id ] ) {
|
||||
return;
|
||||
}
|
||||
// Handle: [ owner, { properties } ] args
|
||||
} else {
|
||||
// In the case where there was actually no "owner" entry and
|
||||
// this.add( owner ) was called to create one, there will be
|
||||
// a corresponding empty plain object in the cache.
|
||||
if ( jQuery.isEmptyObject( this.cache[ index ] ) ) {
|
||||
this.cache[ index ] = data;
|
||||
|
||||
if ( name ) {
|
||||
|
||||
thisCache = pvt ? cache[ id ] : cache[ id ].data;
|
||||
|
||||
if ( thisCache ) {
|
||||
|
||||
// Support array or space separated string names for data keys
|
||||
if ( !jQuery.isArray( name ) ) {
|
||||
|
||||
// try the string as a key before any manipulation
|
||||
if ( name in thisCache ) {
|
||||
name = [ name ];
|
||||
} else {
|
||||
|
||||
// split the camel cased version by spaces unless a key with the spaces exists
|
||||
name = jQuery.camelCase( name );
|
||||
if ( name in thisCache ) {
|
||||
name = [ name ];
|
||||
} else {
|
||||
name = name.split(" ");
|
||||
}
|
||||
}
|
||||
// Otherwise, copy the properties one-by-one to the cache object
|
||||
} else {
|
||||
// If "name" is an array of keys...
|
||||
// When data is initially created, via ("key", "val") signature,
|
||||
// keys will be converted to camelCase.
|
||||
// Since there is no way to tell _how_ a key was added, remove
|
||||
// both plain key and camelCase key. #12786
|
||||
// This will only penalize the array argument path.
|
||||
name = name.concat( jQuery.map( name, jQuery.camelCase ) );
|
||||
}
|
||||
|
||||
for ( i = 0, l = name.length; i < l; i++ ) {
|
||||
delete thisCache[ name[i] ];
|
||||
}
|
||||
|
||||
// If there is no data left in the cache, we want to continue
|
||||
// and let the cache object itself get destroyed
|
||||
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
|
||||
return;
|
||||
for ( prop in data ) {
|
||||
this.cache[ index ][ prop ] = data[ prop ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
get: function( owner, key ) {
|
||||
var cache,
|
||||
index = Data.index( this.owners, owner );
|
||||
|
||||
// See jQuery.data for more information
|
||||
if ( !pvt ) {
|
||||
delete cache[ id ].data;
|
||||
// A valid cache is found, or needs to be created.
|
||||
// New entries will be added and return the current
|
||||
// empty data object to be used as a return reference
|
||||
// return this.add( owner );
|
||||
// This logic was required by expectations made of the
|
||||
// old data system.
|
||||
cache = index === -1 ?
|
||||
this.add( owner ) : this.cache[ index ];
|
||||
|
||||
// Don't destroy the parent cache unless the internal data object
|
||||
// had been the only thing left in it
|
||||
if ( !isEmptyDataObject( cache[ id ] ) ) {
|
||||
return;
|
||||
return key === undefined ?
|
||||
cache : cache[ key ];
|
||||
},
|
||||
access: function( owner, key, value ) {
|
||||
if ( value === undefined && (key && typeof key !== "object") ) {
|
||||
// Assume this is a request to read the cached data
|
||||
return this.get( owner, key );
|
||||
} else {
|
||||
|
||||
// If only an owner was specified, return the entire
|
||||
// cache object.
|
||||
if ( key === undefined ) {
|
||||
return this.get( owner );
|
||||
}
|
||||
|
||||
// Allow setting or extending (existing objects) with an
|
||||
// object of properties, or a key and val
|
||||
this.set( owner, key, value );
|
||||
return value !== undefined ? value : key;
|
||||
}
|
||||
// Otherwise, this is a read request.
|
||||
return this.get( owner, key );
|
||||
},
|
||||
remove: function( owner, key ) {
|
||||
var i, l, name,
|
||||
camel = jQuery.camelCase,
|
||||
index = Data.index( this.owners, owner ),
|
||||
cache = this.cache[ index ];
|
||||
|
||||
if ( key === undefined ) {
|
||||
cache = {};
|
||||
} else {
|
||||
if ( cache ) {
|
||||
// Support array or space separated string of keys
|
||||
if ( !Array.isArray( key ) ) {
|
||||
// Try the string as a key before any manipulation
|
||||
//
|
||||
|
||||
if ( key in cache ) {
|
||||
name = [ key ];
|
||||
} else {
|
||||
// Split the camel cased version by spaces unless a key with the spaces exists
|
||||
name = camel( key );
|
||||
name = name in cache ?
|
||||
[ name ] : name.split(" ");
|
||||
}
|
||||
} else {
|
||||
// If "name" is an array of keys...
|
||||
// When data is initially created, via ("key", "val") signature,
|
||||
// keys will be converted to camelCase.
|
||||
// Since there is no way to tell _how_ a key was added, remove
|
||||
// both plain key and camelCase key. #12786
|
||||
// This will only penalize the array argument path.
|
||||
name = key.concat( key.map( camel ) );
|
||||
}
|
||||
i = 0;
|
||||
l = name.length;
|
||||
|
||||
for ( ; i < l; i++ ) {
|
||||
delete cache[ name[i] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cache[ index ] = cache;
|
||||
},
|
||||
hasData: function( owner ) {
|
||||
var index = Data.index( this.owners, owner );
|
||||
|
||||
if ( index > -1 ) {
|
||||
return !jQuery.isEmptyObject( this.cache[ index ] );
|
||||
}
|
||||
return false;
|
||||
},
|
||||
discard: function( owner ) {
|
||||
var index = Data.index( this.owners, owner );
|
||||
|
||||
if ( index >= 0 ) {
|
||||
this.owners.splice( index, 1 );
|
||||
this.cache.splice( index, 1 );
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// Destroy the cache
|
||||
if ( isNode ) {
|
||||
jQuery.cleanData( [ elem ], true );
|
||||
|
||||
// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
|
||||
} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
|
||||
delete cache[ id ];
|
||||
|
||||
// When all else fails, null
|
||||
} else {
|
||||
cache[ id ] = null;
|
||||
}
|
||||
// This will be used by remove()/cleanData() in manipulation to sever
|
||||
// remaining references to node objects. One day we'll replace the dual
|
||||
// arrays with a WeakMap and this won't be an issue.
|
||||
// (Splices the data objects out of the internal cache arrays)
|
||||
function data_discard( owner ) {
|
||||
data_user.discard( owner );
|
||||
data_priv.discard( owner );
|
||||
}
|
||||
|
||||
// These may be used throughout the jQuery core codebase
|
||||
data_user = new Data();
|
||||
data_priv = new Data();
|
||||
|
||||
|
||||
jQuery.extend({
|
||||
cache: {},
|
||||
|
||||
// This is no longer relevant to jQuery core, but must remain
|
||||
// supported for the sake of jQuery 1.9.x API surface compatibility.
|
||||
acceptData: function() {
|
||||
return true;
|
||||
},
|
||||
// Unique for each copy of jQuery on the page
|
||||
// Non-digits removed to match rinlinejQuery
|
||||
expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
|
||||
|
||||
// The following elements throw uncatchable exceptions if you
|
||||
// attempt to add expando properties to them.
|
||||
noData: {
|
||||
"embed": true,
|
||||
// Ban all objects except for Flash (which handle expandos)
|
||||
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
|
||||
"applet": true
|
||||
},
|
||||
|
||||
hasData: function( elem ) {
|
||||
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
|
||||
return !!elem && !isEmptyDataObject( elem );
|
||||
return data_user.hasData( elem ) || data_priv.hasData( elem );
|
||||
},
|
||||
|
||||
data: function( elem, name, data ) {
|
||||
return internalData( elem, name, data );
|
||||
return data_user.access( elem, name, data );
|
||||
},
|
||||
|
||||
removeData: function( elem, name ) {
|
||||
return internalRemoveData( elem, name );
|
||||
return data_user.remove( elem, name );
|
||||
},
|
||||
|
||||
// For internal use only.
|
||||
// TODO: Replace all calls to _data and _removeData with direct
|
||||
// calls to
|
||||
//
|
||||
// data_priv.access( elem, name, data );
|
||||
//
|
||||
// data_priv.remove( elem, name );
|
||||
//
|
||||
_data: function( elem, name, data ) {
|
||||
return internalData( elem, name, data, true );
|
||||
return data_priv.access( elem, name, data );
|
||||
},
|
||||
|
||||
_removeData: function( elem, name ) {
|
||||
return internalRemoveData( elem, name, true );
|
||||
},
|
||||
|
||||
// A method for determining if a DOM node can handle the data expando
|
||||
acceptData: function( elem ) {
|
||||
// Do not set data on non-element because it will not be cleared (#8335).
|
||||
if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
|
||||
|
||||
// nodes accept data unless otherwise specified; rejection can be conditional
|
||||
return !noData || noData !== true && elem.getAttribute("classid") === noData;
|
||||
return data_priv.remove( elem, name );
|
||||
}
|
||||
});
|
||||
|
||||
@@ -245,20 +209,19 @@ jQuery.fn.extend({
|
||||
// Gets all values
|
||||
if ( key === undefined ) {
|
||||
if ( this.length ) {
|
||||
data = jQuery.data( elem );
|
||||
data = data_user.get( elem );
|
||||
|
||||
if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
|
||||
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
|
||||
attrs = elem.attributes;
|
||||
for ( ; i < attrs.length; i++ ) {
|
||||
name = attrs[i].name;
|
||||
|
||||
if ( !name.indexOf( "data-" ) ) {
|
||||
name = jQuery.camelCase( name.slice(5) );
|
||||
|
||||
if ( name.indexOf( "data-" ) === 0 ) {
|
||||
name = jQuery.camelCase( name.substring(5) );
|
||||
dataAttr( elem, name, data[ name ] );
|
||||
}
|
||||
}
|
||||
jQuery._data( elem, "parsedAttrs", true );
|
||||
data_priv.set( elem, "hasDataAttrs", true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,53 +231,94 @@ jQuery.fn.extend({
|
||||
// Sets multiple values
|
||||
if ( typeof key === "object" ) {
|
||||
return this.each(function() {
|
||||
jQuery.data( this, key );
|
||||
data_user.set( this, key );
|
||||
});
|
||||
}
|
||||
|
||||
return jQuery.access( this, function( value ) {
|
||||
var data,
|
||||
camelKey = jQuery.camelCase( key );
|
||||
|
||||
// Get the Data...
|
||||
if ( value === undefined ) {
|
||||
// Try to fetch any internally stored data first
|
||||
return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
|
||||
|
||||
// Attempt to get data from the cache
|
||||
// with the key as-is
|
||||
data = data_user.get( elem, key );
|
||||
if ( data !== undefined ) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// Attempt to "discover" the data in
|
||||
// HTML5 custom data-* attrs
|
||||
data = dataAttr( elem, key, undefined );
|
||||
if ( data !== undefined ) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// As a last resort, attempt to find
|
||||
// the data by checking AGAIN, but with
|
||||
// a camelCased key.
|
||||
data = data_user.get( elem, camelKey );
|
||||
if ( data !== undefined ) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// We tried really hard, but the data doesn't exist.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Set the data...
|
||||
this.each(function() {
|
||||
jQuery.data( this, key, value );
|
||||
// First, attempt to store a copy or reference of any
|
||||
// data that might've been store with a camelCased key.
|
||||
var data = data_user.get( this, camelKey );
|
||||
|
||||
// For HTML5 data-* attribute interop, we have to
|
||||
// store property names with dashes in a camelCase form.
|
||||
// This might not apply to all properties...*
|
||||
data_user.set( this, camelKey, value );
|
||||
|
||||
// *... In the case of properties that might ACTUALLY
|
||||
// have dashes, we need to also store a copy of that
|
||||
// unchanged property.
|
||||
if ( /-/.test( key ) && data !== undefined ) {
|
||||
data_user.set( this, key, value );
|
||||
}
|
||||
});
|
||||
}, null, value, arguments.length > 1, null, true );
|
||||
},
|
||||
|
||||
removeData: function( key ) {
|
||||
return this.each(function() {
|
||||
jQuery.removeData( this, key );
|
||||
data_user.remove( this, key );
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function dataAttr( elem, key, data ) {
|
||||
var name;
|
||||
|
||||
// If nothing was found internally, try to fetch any
|
||||
// data from the HTML5 data-* attribute
|
||||
if ( data === undefined && elem.nodeType === 1 ) {
|
||||
|
||||
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
|
||||
|
||||
name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
|
||||
data = elem.getAttribute( name );
|
||||
|
||||
if ( typeof data === "string" ) {
|
||||
try {
|
||||
data = data === "true" ? true :
|
||||
data === "false" ? false :
|
||||
data === "null" ? null :
|
||||
// Only convert to a number if it doesn't change the string
|
||||
+data + "" === data ? +data :
|
||||
rbrace.test( data ) ? jQuery.parseJSON( data ) :
|
||||
data;
|
||||
data === "false" ? false :
|
||||
data === "null" ? null :
|
||||
// Only convert to a number if it doesn't change the string
|
||||
+data + "" === data ? +data :
|
||||
rbrace.test( data ) ?
|
||||
JSON.parse( data ) : data;
|
||||
} catch( e ) {}
|
||||
|
||||
// Make sure we set the data so it isn't changed later
|
||||
jQuery.data( elem, key, data );
|
||||
|
||||
data_user.set( elem, key, data );
|
||||
} else {
|
||||
data = undefined;
|
||||
}
|
||||
@@ -322,20 +326,3 @@ function dataAttr( elem, key, data ) {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// checks a cache object for emptiness
|
||||
function isEmptyDataObject( obj ) {
|
||||
var name;
|
||||
for ( name in obj ) {
|
||||
|
||||
// if the public data object is empty, the private is still empty
|
||||
if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( name !== "toJSON" ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ jQuery.event = {
|
||||
var handleObjIn, eventHandle, tmp,
|
||||
events, t, handleObj,
|
||||
special, handlers, type, namespaces, origType,
|
||||
elemData = jQuery._data( elem );
|
||||
elemData = data_priv.get( elem );
|
||||
|
||||
// Don't attach events to noData or text/comment nodes (but allow plain objects)
|
||||
if ( !elemData ) {
|
||||
@@ -130,7 +130,7 @@ jQuery.event = {
|
||||
var j, origCount, tmp,
|
||||
events, t, handleObj,
|
||||
special, handlers, type, namespaces, origType,
|
||||
elemData = jQuery.hasData( elem ) && jQuery._data( elem );
|
||||
elemData = data_priv.hasData( elem ) && data_priv.get( elem );
|
||||
|
||||
if ( !elemData || !(events = elemData.events) ) {
|
||||
return;
|
||||
|
||||
@@ -328,7 +328,7 @@ jQuery.fn.extend({
|
||||
for ( i = 0; i < hasScripts; i++ ) {
|
||||
node = scripts[ i ];
|
||||
if ( rscriptType.test( node.type || "" ) &&
|
||||
!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
|
||||
!data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
|
||||
|
||||
if ( node.src ) {
|
||||
// Hope ajax is available...
|
||||
@@ -511,11 +511,9 @@ jQuery.extend({
|
||||
},
|
||||
|
||||
cleanData: function( elems, /* internal */ acceptData ) {
|
||||
var id, data, elem, type,
|
||||
var data, elem, type,
|
||||
l = elems.length,
|
||||
i = 0,
|
||||
internalKey = jQuery.expando,
|
||||
cache = jQuery.cache,
|
||||
special = jQuery.event.special;
|
||||
|
||||
for ( ; i < l; i++ ) {
|
||||
@@ -523,8 +521,7 @@ jQuery.extend({
|
||||
|
||||
if ( acceptData || jQuery.acceptData( elem ) ) {
|
||||
|
||||
id = elem[ internalKey ];
|
||||
data = id && cache[ id ];
|
||||
data = data_priv.access( elem );
|
||||
|
||||
if ( data ) {
|
||||
for ( type in data.events ) {
|
||||
@@ -536,14 +533,10 @@ jQuery.extend({
|
||||
jQuery.removeEvent( elem, type, data.handle );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove cache only if it was not already removed by jQuery.event.remove
|
||||
if ( cache[ id ] ) {
|
||||
delete cache[ id ];
|
||||
delete elem[ internalKey ];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Discard any remaining `private` and `user` data
|
||||
data_discard( elem );
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -576,38 +569,49 @@ function setGlobalEval( elems, refElements ) {
|
||||
i = 0;
|
||||
|
||||
for ( ; i < l; i++ ) {
|
||||
jQuery._data( elems[ i ], "globalEval", !refElements || jQuery._data( refElements[ i ], "globalEval" ) );
|
||||
data_priv.set(
|
||||
elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function cloneCopyEvent( src, dest ) {
|
||||
var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
|
||||
|
||||
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
|
||||
if ( dest.nodeType !== 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i, l, type,
|
||||
oldData = jQuery._data( src ),
|
||||
curData = jQuery._data( dest, oldData ),
|
||||
events = oldData.events;
|
||||
// 1. Copy private data: events, handlers, etc.
|
||||
if ( data_priv.hasData( src ) ) {
|
||||
pdataOld = data_priv.access( src );
|
||||
pdataCur = jQuery.extend( {}, pdataOld );
|
||||
events = pdataOld.events;
|
||||
|
||||
if ( events ) {
|
||||
delete curData.handle;
|
||||
curData.events = {};
|
||||
data_priv.set( dest, pdataCur );
|
||||
|
||||
for ( type in events ) {
|
||||
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
|
||||
jQuery.event.add( dest, type, events[ type ][ i ] );
|
||||
if ( events ) {
|
||||
delete pdataCur.handle;
|
||||
pdataCur.events = {};
|
||||
|
||||
for ( type in events ) {
|
||||
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
|
||||
jQuery.event.add( dest, type, events[ type ][ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make the cloned public data object a copy from the original
|
||||
if ( curData.data ) {
|
||||
curData.data = jQuery.extend( {}, curData.data );
|
||||
// 2. Copy user data
|
||||
if ( data_user.hasData( src ) ) {
|
||||
udataOld = data_user.access( src );
|
||||
udataCur = jQuery.extend( {}, udataOld );
|
||||
|
||||
data_user.set( dest, udataCur );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getAll( context, tag ) {
|
||||
var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
|
||||
context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
|
||||
|
||||
20
test/data/onbeforeunload.html
Normal file
20
test/data/onbeforeunload.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<script src="/dist/jquery.js"></script>
|
||||
<script>
|
||||
function report( event ) {
|
||||
var payload = {
|
||||
event: event.type
|
||||
};
|
||||
return top.postMessage( JSON.stringify(payload), "*" );
|
||||
}
|
||||
|
||||
jQuery( window ).on( "beforeunload", function( event ) {
|
||||
report( event );
|
||||
}).on( "load", function( event ) {
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 50);
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -200,47 +200,53 @@ var Globals = (function() {
|
||||
*/
|
||||
QUnit.expectJqData = function( elems, key ) {
|
||||
var i, elem, expando;
|
||||
QUnit.current_testEnvironment.checkJqData = true;
|
||||
|
||||
if ( elems.jquery && elems.toArray ) {
|
||||
elems = elems.toArray();
|
||||
}
|
||||
if ( !jQuery.isArray( elems ) ) {
|
||||
elems = [ elems ];
|
||||
}
|
||||
// As of jQuery 2.0, there will be no "cache"-data is
|
||||
// stored and managed completely below the API surface
|
||||
if ( jQuery.cache ) {
|
||||
QUnit.current_testEnvironment.checkJqData = true;
|
||||
|
||||
for ( i = 0; i < elems.length; i++ ) {
|
||||
elem = elems[i];
|
||||
|
||||
// jQuery.data only stores data for nodes in jQuery.cache,
|
||||
// for other data targets the data is stored in the object itself,
|
||||
// in that case we can't test that target for memory leaks.
|
||||
// But we don't have to since in that case the data will/must will
|
||||
// be available as long as the object is not garbage collected by
|
||||
// the js engine, and when it is, the data will be removed with it.
|
||||
if ( !elem.nodeType ) {
|
||||
// Fixes false positives for dataTests(window), dataTests({}).
|
||||
continue;
|
||||
if ( elems.jquery && elems.toArray ) {
|
||||
elems = elems.toArray();
|
||||
}
|
||||
if ( !jQuery.isArray( elems ) ) {
|
||||
elems = [ elems ];
|
||||
}
|
||||
|
||||
expando = elem[ jQuery.expando ];
|
||||
for ( i = 0; i < elems.length; i++ ) {
|
||||
elem = elems[i];
|
||||
|
||||
if ( expando === undefined ) {
|
||||
// In this case the element exists fine, but
|
||||
// jQuery.data (or internal data) was never (in)directly
|
||||
// called.
|
||||
// Since this method was called it means some data was
|
||||
// expected to be found, but since there is nothing, fail early
|
||||
// (instead of in teardown).
|
||||
notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." );
|
||||
} else {
|
||||
if ( expectedDataKeys[expando] ) {
|
||||
expectedDataKeys[expando].push( key );
|
||||
// jQuery.data only stores data for nodes in jQuery.cache,
|
||||
// for other data targets the data is stored in the object itself,
|
||||
// in that case we can't test that target for memory leaks.
|
||||
// But we don't have to since in that case the data will/must will
|
||||
// be available as long as the object is not garbage collected by
|
||||
// the js engine, and when it is, the data will be removed with it.
|
||||
if ( !elem.nodeType ) {
|
||||
// Fixes false positives for dataTests(window), dataTests({}).
|
||||
continue;
|
||||
}
|
||||
|
||||
expando = elem[ jQuery.expando ];
|
||||
|
||||
if ( expando === undefined ) {
|
||||
// In this case the element exists fine, but
|
||||
// jQuery.data (or internal data) was never (in)directly
|
||||
// called.
|
||||
// Since this method was called it means some data was
|
||||
// expected to be found, but since there is nothing, fail early
|
||||
// (instead of in teardown).
|
||||
notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." );
|
||||
} else {
|
||||
expectedDataKeys[expando] = [ key ];
|
||||
if ( expectedDataKeys[expando] ) {
|
||||
expectedDataKeys[expando].push( key );
|
||||
} else {
|
||||
expectedDataKeys[expando] = [ key ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
QUnit.config.urlConfig.push( {
|
||||
id: "jqdata",
|
||||
@@ -334,7 +340,7 @@ var Globals = (function() {
|
||||
} else {
|
||||
delete jQuery.ajaxSettings;
|
||||
}
|
||||
|
||||
|
||||
// Cleanup globals
|
||||
Globals.cleanup();
|
||||
|
||||
|
||||
@@ -6,6 +6,33 @@ test("expando", function(){
|
||||
equal(jQuery.expando !== undefined, true, "jQuery is exposing the expando");
|
||||
});
|
||||
|
||||
test( "jQuery.data & removeData, expected returns", function() {
|
||||
expect(2);
|
||||
|
||||
equal(
|
||||
jQuery.data( document.body, "hello", "world" ), "world",
|
||||
"jQuery.data( elem, key, value ) returns value"
|
||||
);
|
||||
equal(
|
||||
jQuery.removeData( document.body, "hello" ), undefined,
|
||||
"jQuery.removeData( elem, key, value ) returns undefined"
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
test( "jQuery._data & _removeData, expected returns", function() {
|
||||
expect(2);
|
||||
|
||||
equal(
|
||||
jQuery._data( document.body, "hello", "world" ), "world",
|
||||
"jQuery.data( elem, key, value ) returns value"
|
||||
);
|
||||
equal(
|
||||
jQuery._removeData( document.body, "hello" ), undefined,
|
||||
"jQuery.removeData( elem, key, value ) returns undefined"
|
||||
);
|
||||
});
|
||||
|
||||
function dataTests (elem) {
|
||||
var oldCacheLength, dataObj, internalDataObj, expected, actual;
|
||||
|
||||
@@ -106,6 +133,10 @@ test("Data is not being set on comment and text nodes", function() {
|
||||
ok( !jQuery.hasData( jQuery("<span>text</span>").contents().data("foo", 0) ) );
|
||||
|
||||
});
|
||||
/*
|
||||
// Since the new data system does not rely on exandos, limiting the type of
|
||||
// nodes that can have data is no longer necessary. jQuery.acceptData is now irrelevant
|
||||
// and should be removed from the library.
|
||||
|
||||
test("jQuery.acceptData", function() {
|
||||
expect(9);
|
||||
@@ -127,7 +158,7 @@ test("jQuery.acceptData", function() {
|
||||
ok( !jQuery.acceptData( document.createComment("") ), "comment" );
|
||||
ok( !jQuery.acceptData( document.createTextNode("") ), "text" );
|
||||
});
|
||||
|
||||
*/
|
||||
test(".data()", function() {
|
||||
expect(5);
|
||||
|
||||
@@ -424,7 +455,7 @@ if (window.JSON && window.JSON.stringify) {
|
||||
}
|
||||
|
||||
test("jQuery.data should follow html5 specification regarding camel casing", function() {
|
||||
expect(10);
|
||||
expect(12);
|
||||
|
||||
var div = jQuery("<div id='myObject' data-w-t-f='ftw' data-big-a-little-a='bouncing-b' data-foo='a' data-foo-bar='b' data-foo-bar-baz='c'></div>")
|
||||
.prependTo("body");
|
||||
@@ -445,6 +476,9 @@ test("jQuery.data should follow html5 specification regarding camel casing", fun
|
||||
equal( div.data("fooBar"), "d", "Verify updated data-* key" );
|
||||
equal( div.data("foo-bar"), "d", "Verify updated data-* key" );
|
||||
|
||||
equal( div.data("fooBar"), "d", "Verify updated data-* key (fooBar)" );
|
||||
equal( div.data("foo-bar"), "d", "Verify updated data-* key (foo-bar)" );
|
||||
|
||||
div.remove();
|
||||
});
|
||||
|
||||
|
||||
@@ -1371,75 +1371,20 @@ test("Submit event can be stopped (#11049)", function() {
|
||||
|
||||
// Test beforeunload event only if it supported (i.e. not Opera)
|
||||
if ( window.onbeforeunload === null ) {
|
||||
asyncTest("on(beforeunload)", 4, function() {
|
||||
var win,
|
||||
forIE6 = 0,
|
||||
fired = false,
|
||||
iframe = jQuery("<iframe src='data/iframe.html' />");
|
||||
asyncTest("on(beforeunload)", 1, function() {
|
||||
var iframe = jQuery(jQuery.parseHTML("<iframe src='data/onbeforeunload.html'><iframe>"));
|
||||
|
||||
iframe.appendTo("#qunit-fixture").one( "load", function() {
|
||||
win = this.contentWindow || this.contentDocument;
|
||||
window.onmessage = function( event ) {
|
||||
var payload = JSON.parse( event.data );
|
||||
|
||||
jQuery( win ).on( "beforeunload", function() {
|
||||
fired = true;
|
||||
ok( true, "beforeunload event is fired" );
|
||||
});
|
||||
ok( payload.event, "beforeunload", "beforeunload event" );
|
||||
|
||||
strictEqual( win.onbeforeunload, null, "onbeforeunload property on window object still equals null" );
|
||||
iframe.remove();
|
||||
window.onmessage = null;
|
||||
start();
|
||||
};
|
||||
|
||||
// In old Safari beforeunload event will not fire on iframes
|
||||
jQuery( win ).on( "unload", function() {
|
||||
if ( !fired ) {
|
||||
ok( true, "This is suppose to be true only in old Safari" );
|
||||
checker();
|
||||
}
|
||||
});
|
||||
|
||||
jQuery( win ).on( "beforeunload", function() {
|
||||
|
||||
// On iframe in IE6 beforeunload event will not fire if event is binded through window object,
|
||||
// nevertheless, test should continue
|
||||
window.setTimeout(function() {
|
||||
if ( !forIE6 ) {
|
||||
checker();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
win.onbeforeunload = function() {
|
||||
if ( !forIE6 ) {
|
||||
forIE6++;
|
||||
checker();
|
||||
}
|
||||
};
|
||||
|
||||
function checker() {
|
||||
ok( true, "window.onbeforeunload handler is called" );
|
||||
iframe = jQuery("<iframe src='data/iframe.html' />");
|
||||
|
||||
iframe.appendTo("#qunit-fixture").one( "load", function() {
|
||||
win = iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument;
|
||||
|
||||
jQuery( win ).on( "beforeunload", function() {
|
||||
strictEqual( win.onbeforeunload, null, "Event handler is fired, even when onbeforeunload property on window is nulled" );
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
jQuery( win ).on( "unload", function() {
|
||||
if ( !fired ) {
|
||||
jQuery( win ).trigger("beforeunload");
|
||||
}
|
||||
});
|
||||
|
||||
win.onbeforeunload = null;
|
||||
|
||||
win.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
win.location.reload();
|
||||
});
|
||||
iframe.appendTo("#qunit-fixture");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2567,7 +2512,7 @@ test( "Namespace preserved when passed an Event (#12739)", function() {
|
||||
e.handled = true;
|
||||
equal( e.namespace, "bar", "namespace is bar" );
|
||||
jQuery( e.target ).find("div").each(function() {
|
||||
jQuery( this ).triggerHandler( e );
|
||||
jQuery( this ).triggerHandler( e );
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user