decoupling styles retrieval from the attr method
[jquery.git] / src / attributes.js
1 jQuery.fn.extend({
2         attr: function( name, value ) {
3                 var options = name, isFunction = jQuery.isFunction( value );
4
5                 if ( typeof name === "string" ) {
6                         // Are we setting the attribute?
7                         if ( value === undefined ) {
8                                 return this.length ?
9                                         jQuery.attr( this[0], name ) :
10                                         null;
11
12                         // Convert name, value params to options hash format
13                         } else {
14                                 options = {};
15                                 options[ name ] = value;
16                         }
17                 }
18
19                 // For each element...
20                 for ( var i = 0, l = this.length; i < l; i++ ) {
21                         var elem = this[i];
22
23                         // Set all the attributes
24                         for ( var prop in options ) {
25                                 value = options[prop];
26
27                                 if ( isFunction ) {
28                                         value = value.call( elem, i );
29                                 }
30
31                                 jQuery.attr( elem, prop, value );
32                         }
33                 }
34
35                 return this;
36         },
37
38         hasClass: function( selector ) {
39                 return !!selector && this.is( "." + selector );
40         },
41
42         val: function( value ) {
43                 if ( value === undefined ) {
44                         var elem = this[0];
45
46                         if ( elem ) {
47                                 if( jQuery.nodeName( elem, 'option' ) )
48                                         return (elem.attributes.value || {}).specified ? elem.value : elem.text;
49                                 
50                                 // We need to handle select boxes special
51                                 if ( jQuery.nodeName( elem, "select" ) ) {
52                                         var index = elem.selectedIndex,
53                                                 values = [],
54                                                 options = elem.options,
55                                                 one = elem.type == "select-one";
56
57                                         // Nothing was selected
58                                         if ( index < 0 )
59                                                 return null;
60
61                                         // Loop through all the selected options
62                                         for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
63                                                 var option = options[ i ];
64
65                                                 if ( option.selected ) {
66                                                         // Get the specifc value for the option
67                                                         value = jQuery(option).val();
68
69                                                         // We don't need an array for one selects
70                                                         if ( one )
71                                                                 return value;
72
73                                                         // Multi-Selects return an array
74                                                         values.push( value );
75                                                 }
76                                         }
77
78                                         return values;                          
79                                 }
80
81                                 // Everything else, we just grab the value
82                                 return (elem.value || "").replace(/\r/g, "");
83
84                         }
85
86                         return undefined;
87                 }
88
89                 if ( typeof value === "number" )
90                         value += '';
91
92                 return this.each(function(){
93                         if ( this.nodeType != 1 )
94                                 return;
95
96                         if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
97                                 this.checked = (jQuery.inArray(this.value, value) >= 0 ||
98                                         jQuery.inArray(this.name, value) >= 0);
99
100                         else if ( jQuery.nodeName( this, "select" ) ) {
101                                 var values = jQuery.makeArray(value);
102
103                                 jQuery( "option", this ).each(function(){
104                                         this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
105                                                 jQuery.inArray( this.text, values ) >= 0);
106                                 });
107
108                                 if ( !values.length )
109                                         this.selectedIndex = -1;
110
111                         } else
112                                 this.value = value;
113                 });
114         }
115 });
116
117 jQuery.each({
118         removeAttr: function( name ) {
119                 jQuery.attr( this, name, "" );
120                 if (this.nodeType == 1)
121                         this.removeAttribute( name );
122         },
123
124         addClass: function( classNames ) {
125                 jQuery.className.add( this, classNames );
126         },
127
128         removeClass: function( classNames ) {
129                 jQuery.className.remove( this, classNames );
130         },
131
132         toggleClass: function( classNames, state ) {
133                 if( typeof state !== "boolean" )
134                         state = !jQuery.className.has( this, classNames );
135                 jQuery.className[ state ? "add" : "remove" ]( this, classNames );
136         }
137 }, function(name, fn){
138         jQuery.fn[ name ] = function(){
139                 return this.each( fn, arguments );
140         };
141 });
142
143 jQuery.extend({
144         className: {
145                 // internal only, use addClass("class")
146                 add: function( elem, classNames ) {
147                         jQuery.each((classNames || "").split(/\s+/), function(i, className){
148                                 if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
149                                         elem.className += (elem.className ? " " : "") + className;
150                         });
151                 },
152
153                 // internal only, use removeClass("class")
154                 remove: function( elem, classNames ) {
155                         if (elem.nodeType == 1)
156                                 elem.className = classNames !== undefined ?
157                                         jQuery.grep(elem.className.split(/\s+/), function(className){
158                                                 return !jQuery.className.has( classNames, className );
159                                         }).join(" ") :
160                                         "";
161                 },
162
163                 // internal only, use hasClass("class")
164                 has: function( elem, className ) {
165                         return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
166                 }
167         },
168
169         attr: function( elem, name, value ) {
170                 // don't set attributes on text and comment nodes
171                 if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
172                         return undefined;
173
174                 var notxml = !elem.tagName || !jQuery.isXMLDoc( elem ),
175                         // Whether we are setting (or getting)
176                         set = value !== undefined;
177
178                 // Try to normalize/fix the name
179                 name = notxml && jQuery.props[ name ] || name;
180
181                 // Only do all the following if this is a node (faster for style)
182                 if ( elem.tagName ) {
183
184                         // These attributes require special treatment
185                         var special = /href|src|style/.test( name );
186
187                         // Safari mis-reports the default selected property of a hidden option
188                         // Accessing the parent's selectedIndex property fixes it
189                         if ( name == "selected" && elem.parentNode )
190                                 elem.parentNode.selectedIndex;
191
192                         // If applicable, access the attribute via the DOM 0 way
193                         if ( name in elem && notxml && !special ) {
194                                 if ( set ){
195                                         // We can't allow the type property to be changed (since it causes problems in IE)
196                                         if ( name == "type" && elem.nodeName.match(/(button|input)/i) && elem.parentNode )
197                                                 throw "type property can't be changed";
198
199                                         elem[ name ] = value;
200                                 }
201
202                                 // browsers index elements by id/name on forms, give priority to attributes.
203                                 if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
204                                         return elem.getAttributeNode( name ).nodeValue;
205
206                                 // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
207                                 // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
208                                 if ( name == "tabIndex" ) {
209                                         var attributeNode = elem.getAttributeNode( "tabIndex" );
210                                         return attributeNode && attributeNode.specified
211                                                 ? attributeNode.value
212                                                 : elem.nodeName.match(/(button|input|object|select|textarea)/i)
213                                                         ? 0
214                                                         : elem.nodeName.match(/^(a|area)$/i) && elem.href
215                                                                 ? 0
216                                                                 : undefined;
217                                 }
218
219                                 return elem[ name ];
220                         }
221
222                         if ( !jQuery.support.style && notxml && name == "style" ) {
223                                 if ( set )
224                                         elem.style.cssText = "" + value;
225
226                                 return elem.style.cssText;
227                         }
228
229                         if ( set )
230                                 // convert the value to a string (all browsers do this but IE) see #1070
231                                 elem.setAttribute( name, "" + value );
232
233                         var attr = !jQuery.support.hrefNormalized && notxml && special
234                                         // Some attributes require a special call on IE
235                                         ? elem.getAttribute( name, 2 )
236                                         : elem.getAttribute( name );
237
238                         // Non-existent attributes return null, we normalize to undefined
239                         return attr === null ? undefined : attr;
240                 }
241
242                 // elem is actually elem.style ... set the style
243                 // Using attr for specific style information is now deprecated. Use style insead.
244                 return jQuery.style(elem, name, value);
245         }
246 });