Data.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. define([
  2. "../core",
  3. "../var/rnotwhite",
  4. "./accepts"
  5. ], function( jQuery, rnotwhite ) {
  6. function Data() {
  7. // Support: Android<4,
  8. // Old WebKit does not have Object.preventExtensions/freeze method,
  9. // return new empty object instead with no [[set]] accessor
  10. Object.defineProperty( this.cache = {}, 0, {
  11. get: function() {
  12. return {};
  13. }
  14. });
  15. this.expando = jQuery.expando + Data.uid++;
  16. }
  17. Data.uid = 1;
  18. Data.accepts = jQuery.acceptData;
  19. Data.prototype = {
  20. key: function( owner ) {
  21. // We can accept data for non-element nodes in modern browsers,
  22. // but we should not, see #8335.
  23. // Always return the key for a frozen object.
  24. if ( !Data.accepts( owner ) ) {
  25. return 0;
  26. }
  27. var descriptor = {},
  28. // Check if the owner object already has a cache key
  29. unlock = owner[ this.expando ];
  30. // If not, create one
  31. if ( !unlock ) {
  32. unlock = Data.uid++;
  33. // Secure it in a non-enumerable, non-writable property
  34. try {
  35. descriptor[ this.expando ] = { value: unlock };
  36. Object.defineProperties( owner, descriptor );
  37. // Support: Android<4
  38. // Fallback to a less secure definition
  39. } catch ( e ) {
  40. descriptor[ this.expando ] = unlock;
  41. jQuery.extend( owner, descriptor );
  42. }
  43. }
  44. // Ensure the cache object
  45. if ( !this.cache[ unlock ] ) {
  46. this.cache[ unlock ] = {};
  47. }
  48. return unlock;
  49. },
  50. set: function( owner, data, value ) {
  51. var prop,
  52. // There may be an unlock assigned to this node,
  53. // if there is no entry for this "owner", create one inline
  54. // and set the unlock as though an owner entry had always existed
  55. unlock = this.key( owner ),
  56. cache = this.cache[ unlock ];
  57. // Handle: [ owner, key, value ] args
  58. if ( typeof data === "string" ) {
  59. cache[ data ] = value;
  60. // Handle: [ owner, { properties } ] args
  61. } else {
  62. // Fresh assignments by object are shallow copied
  63. if ( jQuery.isEmptyObject( cache ) ) {
  64. jQuery.extend( this.cache[ unlock ], data );
  65. // Otherwise, copy the properties one-by-one to the cache object
  66. } else {
  67. for ( prop in data ) {
  68. cache[ prop ] = data[ prop ];
  69. }
  70. }
  71. }
  72. return cache;
  73. },
  74. get: function( owner, key ) {
  75. // Either a valid cache is found, or will be created.
  76. // New caches will be created and the unlock returned,
  77. // allowing direct access to the newly created
  78. // empty data object. A valid owner object must be provided.
  79. var cache = this.cache[ this.key( owner ) ];
  80. return key === undefined ?
  81. cache : cache[ key ];
  82. },
  83. access: function( owner, key, value ) {
  84. var stored;
  85. // In cases where either:
  86. //
  87. // 1. No key was specified
  88. // 2. A string key was specified, but no value provided
  89. //
  90. // Take the "read" path and allow the get method to determine
  91. // which value to return, respectively either:
  92. //
  93. // 1. The entire cache object
  94. // 2. The data stored at the key
  95. //
  96. if ( key === undefined ||
  97. ((key && typeof key === "string") && value === undefined) ) {
  98. stored = this.get( owner, key );
  99. return stored !== undefined ?
  100. stored : this.get( owner, jQuery.camelCase(key) );
  101. }
  102. // [*]When the key is not a string, or both a key and value
  103. // are specified, set or extend (existing objects) with either:
  104. //
  105. // 1. An object of properties
  106. // 2. A key and value
  107. //
  108. this.set( owner, key, value );
  109. // Since the "set" path can have two possible entry points
  110. // return the expected data based on which path was taken[*]
  111. return value !== undefined ? value : key;
  112. },
  113. remove: function( owner, key ) {
  114. var i, name, camel,
  115. unlock = this.key( owner ),
  116. cache = this.cache[ unlock ];
  117. if ( key === undefined ) {
  118. this.cache[ unlock ] = {};
  119. } else {
  120. // Support array or space separated string of keys
  121. if ( jQuery.isArray( key ) ) {
  122. // If "name" is an array of keys...
  123. // When data is initially created, via ("key", "val") signature,
  124. // keys will be converted to camelCase.
  125. // Since there is no way to tell _how_ a key was added, remove
  126. // both plain key and camelCase key. #12786
  127. // This will only penalize the array argument path.
  128. name = key.concat( key.map( jQuery.camelCase ) );
  129. } else {
  130. camel = jQuery.camelCase( key );
  131. // Try the string as a key before any manipulation
  132. if ( key in cache ) {
  133. name = [ key, camel ];
  134. } else {
  135. // If a key with the spaces exists, use it.
  136. // Otherwise, create an array by matching non-whitespace
  137. name = camel;
  138. name = name in cache ?
  139. [ name ] : ( name.match( rnotwhite ) || [] );
  140. }
  141. }
  142. i = name.length;
  143. while ( i-- ) {
  144. delete cache[ name[ i ] ];
  145. }
  146. }
  147. },
  148. hasData: function( owner ) {
  149. return !jQuery.isEmptyObject(
  150. this.cache[ owner[ this.expando ] ] || {}
  151. );
  152. },
  153. discard: function( owner ) {
  154. if ( owner[ this.expando ] ) {
  155. delete this.cache[ owner[ this.expando ] ];
  156. }
  157. }
  158. };
  159. return Data;
  160. });