mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
326 lines
8.4 KiB
JavaScript
326 lines
8.4 KiB
JavaScript
var data_user, data_priv,
|
|
rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
|
|
rmultiDash = /([A-Z])/g;
|
|
|
|
function Data() {
|
|
// Nodes|Objects
|
|
this.owners = [];
|
|
// Data objects
|
|
this.cache = [];
|
|
}
|
|
|
|
Data.index = function( array, node ) {
|
|
return array.indexOf( node );
|
|
};
|
|
|
|
|
|
Data.prototype = {
|
|
add: function( owner ) {
|
|
return (this.cache[ this.owners.push( owner ) - 1 ] = {});
|
|
},
|
|
set: function( owner, data, value ) {
|
|
var prop,
|
|
index = Data.index( this.owners, owner );
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
|
|
// Otherwise, copy the properties one-by-one to the cache object
|
|
} else {
|
|
for ( prop in data ) {
|
|
this.cache[ index ][ prop ] = data[ prop ];
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
get: function( owner, key ) {
|
|
var cache,
|
|
index = Data.index( this.owners, owner );
|
|
|
|
// 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 ];
|
|
|
|
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 {
|
|
// If a key with the spaces exists, use it.
|
|
// Otherwise, create an array by matching non-whitespace
|
|
name = camel( key );
|
|
name = name in cache ?
|
|
[ name ] : ( name.match( core_rnotwhite ) || [] );
|
|
}
|
|
} 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 );
|
|
|
|
return index !== -1 && !jQuery.isEmptyObject( this.cache[ index ] );
|
|
},
|
|
discard: function( owner ) {
|
|
var index = Data.index( this.owners, owner );
|
|
|
|
if ( index !== -1 ) {
|
|
this.owners.splice( index, 1 );
|
|
this.cache.splice( index, 1 );
|
|
}
|
|
return this;
|
|
}
|
|
};
|
|
|
|
// 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({
|
|
// 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, "" ),
|
|
|
|
hasData: function( elem ) {
|
|
return data_user.hasData( elem ) || data_priv.hasData( elem );
|
|
},
|
|
|
|
data: function( elem, name, data ) {
|
|
return data_user.access( elem, name, data );
|
|
},
|
|
|
|
removeData: function( elem, name ) {
|
|
return data_user.remove( elem, name );
|
|
},
|
|
|
|
// 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 data_priv.access( elem, name, data );
|
|
},
|
|
|
|
_removeData: function( elem, name ) {
|
|
return data_priv.remove( elem, name );
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
data: function( key, value ) {
|
|
var attrs, name,
|
|
elem = this[0],
|
|
i = 0,
|
|
data = null;
|
|
|
|
// Gets all values
|
|
if ( key === undefined ) {
|
|
if ( this.length ) {
|
|
data = data_user.get( elem );
|
|
|
|
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-" ) === 0 ) {
|
|
name = jQuery.camelCase( name.substring(5) );
|
|
dataAttr( elem, name, data[ name ] );
|
|
}
|
|
}
|
|
data_priv.set( elem, "hasDataAttrs", true );
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Sets multiple values
|
|
if ( typeof key === "object" ) {
|
|
return this.each(function() {
|
|
data_user.set( this, key );
|
|
});
|
|
}
|
|
|
|
return jQuery.access( this, function( value ) {
|
|
var data,
|
|
camelKey = jQuery.camelCase( key );
|
|
|
|
// Get the Data...
|
|
if ( value === undefined ) {
|
|
|
|
// 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() {
|
|
// 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() {
|
|
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 ) {
|
|
|
|
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 ) ?
|
|
JSON.parse( data ) : data;
|
|
} catch( e ) {}
|
|
|
|
// Make sure we set the data so it isn't changed later
|
|
data_user.set( elem, key, data );
|
|
} else {
|
|
data = undefined;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|