data.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. define( [
  2. "./core",
  3. "./var/deletedIds",
  4. "./data/support",
  5. "./data/var/acceptData"
  6. ], function( jQuery, deletedIds, support, acceptData ) {
  7. var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
  8. rmultiDash = /([A-Z])/g;
  9. function dataAttr( elem, key, data ) {
  10. // If nothing was found internally, try to fetch any
  11. // data from the HTML5 data-* attribute
  12. if ( data === undefined && elem.nodeType === 1 ) {
  13. var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
  14. data = elem.getAttribute( name );
  15. if ( typeof data === "string" ) {
  16. try {
  17. data = data === "true" ? true :
  18. data === "false" ? false :
  19. data === "null" ? null :
  20. // Only convert to a number if it doesn't change the string
  21. +data + "" === data ? +data :
  22. rbrace.test( data ) ? jQuery.parseJSON( data ) :
  23. data;
  24. } catch ( e ) {}
  25. // Make sure we set the data so it isn't changed later
  26. jQuery.data( elem, key, data );
  27. } else {
  28. data = undefined;
  29. }
  30. }
  31. return data;
  32. }
  33. // checks a cache object for emptiness
  34. function isEmptyDataObject( obj ) {
  35. var name;
  36. for ( name in obj ) {
  37. // if the public data object is empty, the private is still empty
  38. if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) {
  39. continue;
  40. }
  41. if ( name !== "toJSON" ) {
  42. return false;
  43. }
  44. }
  45. return true;
  46. }
  47. function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
  48. if ( !acceptData( elem ) ) {
  49. return;
  50. }
  51. var ret, thisCache,
  52. internalKey = jQuery.expando,
  53. // We have to handle DOM nodes and JS objects differently because IE6-7
  54. // can't GC object references properly across the DOM-JS boundary
  55. isNode = elem.nodeType,
  56. // Only DOM nodes need the global jQuery cache; JS object data is
  57. // attached directly to the object so GC can occur automatically
  58. cache = isNode ? jQuery.cache : elem,
  59. // Only defining an ID for JS objects if its cache already exists allows
  60. // the code to shortcut on the same path as a DOM node with no cache
  61. id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
  62. // Avoid doing any more work than we need to when trying to get data on an
  63. // object that has no data at all
  64. if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) &&
  65. data === undefined && typeof name === "string" ) {
  66. return;
  67. }
  68. if ( !id ) {
  69. // Only DOM nodes need a new unique ID for each element since their data
  70. // ends up in the global cache
  71. if ( isNode ) {
  72. id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
  73. } else {
  74. id = internalKey;
  75. }
  76. }
  77. if ( !cache[ id ] ) {
  78. // Avoid exposing jQuery metadata on plain JS objects when the object
  79. // is serialized using JSON.stringify
  80. cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
  81. }
  82. // An object can be passed to jQuery.data instead of a key/value pair; this gets
  83. // shallow copied over onto the existing cache
  84. if ( typeof name === "object" || typeof name === "function" ) {
  85. if ( pvt ) {
  86. cache[ id ] = jQuery.extend( cache[ id ], name );
  87. } else {
  88. cache[ id ].data = jQuery.extend( cache[ id ].data, name );
  89. }
  90. }
  91. thisCache = cache[ id ];
  92. // jQuery data() is stored in a separate object inside the object's internal data
  93. // cache in order to avoid key collisions between internal data and user-defined
  94. // data.
  95. if ( !pvt ) {
  96. if ( !thisCache.data ) {
  97. thisCache.data = {};
  98. }
  99. thisCache = thisCache.data;
  100. }
  101. if ( data !== undefined ) {
  102. thisCache[ jQuery.camelCase( name ) ] = data;
  103. }
  104. // Check for both converted-to-camel and non-converted data property names
  105. // If a data property was specified
  106. if ( typeof name === "string" ) {
  107. // First Try to find as-is property data
  108. ret = thisCache[ name ];
  109. // Test for null|undefined property data
  110. if ( ret == null ) {
  111. // Try to find the camelCased property
  112. ret = thisCache[ jQuery.camelCase( name ) ];
  113. }
  114. } else {
  115. ret = thisCache;
  116. }
  117. return ret;
  118. }
  119. function internalRemoveData( elem, name, pvt ) {
  120. if ( !acceptData( elem ) ) {
  121. return;
  122. }
  123. var thisCache, i,
  124. isNode = elem.nodeType,
  125. // See jQuery.data for more information
  126. cache = isNode ? jQuery.cache : elem,
  127. id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
  128. // If there is already no cache entry for this object, there is no
  129. // purpose in continuing
  130. if ( !cache[ id ] ) {
  131. return;
  132. }
  133. if ( name ) {
  134. thisCache = pvt ? cache[ id ] : cache[ id ].data;
  135. if ( thisCache ) {
  136. // Support array or space separated string names for data keys
  137. if ( !jQuery.isArray( name ) ) {
  138. // try the string as a key before any manipulation
  139. if ( name in thisCache ) {
  140. name = [ name ];
  141. } else {
  142. // split the camel cased version by spaces unless a key with the spaces exists
  143. name = jQuery.camelCase( name );
  144. if ( name in thisCache ) {
  145. name = [ name ];
  146. } else {
  147. name = name.split( " " );
  148. }
  149. }
  150. } else {
  151. // If "name" is an array of keys...
  152. // When data is initially created, via ("key", "val") signature,
  153. // keys will be converted to camelCase.
  154. // Since there is no way to tell _how_ a key was added, remove
  155. // both plain key and camelCase key. #12786
  156. // This will only penalize the array argument path.
  157. name = name.concat( jQuery.map( name, jQuery.camelCase ) );
  158. }
  159. i = name.length;
  160. while ( i-- ) {
  161. delete thisCache[ name[ i ] ];
  162. }
  163. // If there is no data left in the cache, we want to continue
  164. // and let the cache object itself get destroyed
  165. if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) {
  166. return;
  167. }
  168. }
  169. }
  170. // See jQuery.data for more information
  171. if ( !pvt ) {
  172. delete cache[ id ].data;
  173. // Don't destroy the parent cache unless the internal data object
  174. // had been the only thing left in it
  175. if ( !isEmptyDataObject( cache[ id ] ) ) {
  176. return;
  177. }
  178. }
  179. // Destroy the cache
  180. if ( isNode ) {
  181. jQuery.cleanData( [ elem ], true );
  182. // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
  183. /* jshint eqeqeq: false */
  184. } else if ( support.deleteExpando || cache != cache.window ) {
  185. /* jshint eqeqeq: true */
  186. delete cache[ id ];
  187. // When all else fails, undefined
  188. } else {
  189. cache[ id ] = undefined;
  190. }
  191. }
  192. jQuery.extend( {
  193. cache: {},
  194. // The following elements (space-suffixed to avoid Object.prototype collisions)
  195. // throw uncatchable exceptions if you attempt to set expando properties
  196. noData: {
  197. "applet ": true,
  198. "embed ": true,
  199. // ...but Flash objects (which have this classid) *can* handle expandos
  200. "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
  201. },
  202. hasData: function( elem ) {
  203. elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ];
  204. return !!elem && !isEmptyDataObject( elem );
  205. },
  206. data: function( elem, name, data ) {
  207. return internalData( elem, name, data );
  208. },
  209. removeData: function( elem, name ) {
  210. return internalRemoveData( elem, name );
  211. },
  212. // For internal use only.
  213. _data: function( elem, name, data ) {
  214. return internalData( elem, name, data, true );
  215. },
  216. _removeData: function( elem, name ) {
  217. return internalRemoveData( elem, name, true );
  218. }
  219. } );
  220. jQuery.fn.extend( {
  221. data: function( key, value ) {
  222. var i, name, data,
  223. elem = this[ 0 ],
  224. attrs = elem && elem.attributes;
  225. // Special expections of .data basically thwart jQuery.access,
  226. // so implement the relevant behavior ourselves
  227. // Gets all values
  228. if ( key === undefined ) {
  229. if ( this.length ) {
  230. data = jQuery.data( elem );
  231. if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
  232. i = attrs.length;
  233. while ( i-- ) {
  234. // Support: IE11+
  235. // The attrs elements can be null (#14894)
  236. if ( attrs[ i ] ) {
  237. name = attrs[ i ].name;
  238. if ( name.indexOf( "data-" ) === 0 ) {
  239. name = jQuery.camelCase( name.slice( 5 ) );
  240. dataAttr( elem, name, data[ name ] );
  241. }
  242. }
  243. }
  244. jQuery._data( elem, "parsedAttrs", true );
  245. }
  246. }
  247. return data;
  248. }
  249. // Sets multiple values
  250. if ( typeof key === "object" ) {
  251. return this.each( function() {
  252. jQuery.data( this, key );
  253. } );
  254. }
  255. return arguments.length > 1 ?
  256. // Sets one value
  257. this.each( function() {
  258. jQuery.data( this, key, value );
  259. } ) :
  260. // Gets one value
  261. // Try to fetch any internally stored data first
  262. elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
  263. },
  264. removeData: function( key ) {
  265. return this.each( function() {
  266. jQuery.removeData( this, key );
  267. } );
  268. }
  269. } );
  270. return jQuery;
  271. } );