Make .attr(name, null) equivalent to removeAttr(name). (Was roughly this before ...
[jquery.git] / src / data.js
1 (function( jQuery ) {
2
3 var windowData = {},
4         rbrace = /^(?:\{.*\}|\[.*\])$/;
5
6 jQuery.extend({
7         cache: {},
8
9         // Please use with caution
10         uuid: 0,
11
12         // Unique for each copy of jQuery on the page   
13         expando: "jQuery" + jQuery.now(),
14
15         // The following elements throw uncatchable exceptions if you
16         // attempt to add expando properties to them.
17         noData: {
18                 "embed": true,
19                 // Ban all objects except for Flash (which handle expandos)
20                 "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
21                 "applet": true
22         },
23
24         data: function( elem, name, data ) {
25                 if ( !jQuery.acceptData( elem ) ) {
26                         return;
27                 }
28
29                 elem = elem == window ?
30                         windowData :
31                         elem;
32
33                 var id = elem[ jQuery.expando ], cache = jQuery.cache, thisCache,
34                         isNode = elem.nodeType,
35                         store;
36
37                 if ( !id && typeof name === "string" && data === undefined ) {
38                         return;
39                 }
40
41                 // Get the data from the object directly
42                 if ( !isNode ) {
43                         cache = elem;
44                         id = jQuery.expando;
45
46                 // Compute a unique ID for the element
47                 } else if ( !id ) {
48                         elem[ jQuery.expando ] = id = ++jQuery.uuid;
49                 }
50
51                 // Avoid generating a new cache unless none exists and we
52                 // want to manipulate it.
53                 if ( typeof name === "object" ) {
54                         if ( isNode ) {
55                                 cache[ id ] = jQuery.extend(cache[ id ], name);
56
57                         } else {
58                                 store = jQuery.extend(cache[ id ], name);
59                                 cache[ id ] = function() {
60                                         return store;
61                                 };
62                         }
63
64                 } else if ( !cache[ id ] ) {
65                         if ( isNode ) {
66                                 cache[ id ] = {};
67
68                         } else {
69                                 store = {};
70                                 cache[ id ] = function() {
71                                         return store;
72                                 };
73                         }
74                         
75                 }
76
77                 thisCache = isNode ? cache[ id ] : cache[ id ]();
78
79                 // Prevent overriding the named cache with undefined values
80                 if ( data !== undefined ) {
81                         thisCache[ name ] = data;
82                 }
83
84                 return typeof name === "string" ? thisCache[ name ] : thisCache;
85         },
86
87         removeData: function( elem, name ) {
88                 if ( !jQuery.acceptData( elem ) ) {
89                         return;
90                 }
91
92                 elem = elem == window ?
93                         windowData :
94                         elem;
95
96                 var isNode = elem.nodeType,
97                         id = elem[ jQuery.expando ], cache = jQuery.cache;
98                 if ( id && !isNode ) {
99                         id = id();
100                 }
101                 var thisCache = cache[ id ];
102
103                 // If we want to remove a specific section of the element's data
104                 if ( name ) {
105                         if ( thisCache ) {
106                                 // Remove the section of cache data
107                                 delete thisCache[ name ];
108
109                                 // If we've removed all the data, remove the element's cache
110                                 if ( jQuery.isEmptyObject(thisCache) ) {
111                                         jQuery.removeData( elem );
112                                 }
113                         }
114
115                 // Otherwise, we want to remove all of the element's data
116                 } else {
117                         if ( jQuery.support.deleteExpando || !isNode ) {
118                                 delete elem[ jQuery.expando ];
119
120                         } else if ( elem.removeAttribute ) {
121                                 elem.removeAttribute( jQuery.expando );
122                         }
123
124                         // Completely remove the data cache
125                         if ( isNode ) {
126                                 delete cache[ id ];
127                         }
128                 }
129         },
130
131         // A method for determining if a DOM node can handle the data expando
132         acceptData: function( elem ) {
133                 if ( elem.nodeName ) {
134                         match = jQuery.noData[ elem.nodeName.toLowerCase() ];
135
136                         if ( match ) {
137                                 return !(match === true || elem.getAttribute("classid") !== match);
138                         }
139                 }
140
141                 return true;
142         }
143 });
144
145 jQuery.fn.extend({
146         data: function( key, value ) {
147                 if ( typeof key === "undefined" ) {
148                         return this.length ? jQuery.data( this[0] ) : null;
149
150                 } else if ( typeof key === "object" ) {
151                         return this.each(function() {
152                                 jQuery.data( this, key );
153                         });
154                 }
155
156                 var parts = key.split(".");
157                 parts[1] = parts[1] ? "." + parts[1] : "";
158
159                 if ( value === undefined ) {
160                         var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
161
162                         // Try to fetch any internally stored data first
163                         if ( data === undefined && this.length ) {
164                                 data = jQuery.data( this[0], key );
165
166                                 // If nothing was found internally, try to fetch any
167                                 // data from the HTML5 data-* attribute
168                                 if ( data === undefined && this[0].nodeType === 1 ) {
169                                         data = this[0].getAttribute( "data-" + key );
170
171                                         if ( typeof data === "string" ) {
172                                                 try {
173                                                         data = data === "true" ? true :
174                                                                 data === "false" ? false :
175                                                                 data === "null" ? null :
176                                                                 !jQuery.isNaN( data ) ? parseFloat( data ) :
177                                                                 rbrace.test( data ) ? jQuery.parseJSON( data ) :
178                                                                 data;
179                                                 } catch( e ) {}
180
181                                         } else {
182                                                 data = undefined;
183                                         }
184                                 }
185                         }
186
187                         return data === undefined && parts[1] ?
188                                 this.data( parts[0] ) :
189                                 data;
190
191                 } else {
192                         return this.each(function() {
193                                 var $this = jQuery( this ), args = [ parts[0], value ];
194
195                                 $this.triggerHandler( "setData" + parts[1] + "!", args );
196                                 jQuery.data( this, key, value );
197                                 $this.triggerHandler( "changeData" + parts[1] + "!", args );
198                         });
199                 }
200         },
201
202         removeData: function( key ) {
203                 return this.each(function() {
204                         jQuery.removeData( this, key );
205                 });
206         }
207 });
208
209 })( jQuery );