PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/src/attributes.js

https://bitbucket.org/wwag110465/jquery
JavaScript | 498 lines | 374 code | 79 blank | 45 comment | 125 complexity | 07e3d52a0809fe98979d593828d9911b MD5 | raw file
  1. var nodeHook, boolHook,
  2. rclass = /[\t\r\n]/g,
  3. rreturn = /\r/g,
  4. rfocusable = /^(?:input|select|textarea|button|object)$/i,
  5. rclickable = /^(?:a|area)$/i,
  6. rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i;
  7. jQuery.fn.extend({
  8. attr: function( name, value ) {
  9. return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
  10. },
  11. removeAttr: function( name ) {
  12. return this.each(function() {
  13. jQuery.removeAttr( this, name );
  14. });
  15. },
  16. prop: function( name, value ) {
  17. return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
  18. },
  19. removeProp: function( name ) {
  20. name = jQuery.propFix[ name ] || name;
  21. return this.each(function() {
  22. // try/catch handles cases where IE balks (such as removing a property on window)
  23. try {
  24. this[ name ] = undefined;
  25. delete this[ name ];
  26. } catch( e ) {}
  27. });
  28. },
  29. addClass: function( value ) {
  30. var classes, elem, cur, clazz, j,
  31. i = 0,
  32. len = this.length,
  33. proceed = typeof value === "string" && value;
  34. if ( jQuery.isFunction( value ) ) {
  35. return this.each(function( j ) {
  36. jQuery( this ).addClass( value.call( this, j, this.className ) );
  37. });
  38. }
  39. if ( proceed ) {
  40. // The disjunction here is for better compressibility (see removeClass)
  41. classes = ( value || "" ).match( core_rnotwhite ) || [];
  42. for ( ; i < len; i++ ) {
  43. elem = this[ i ];
  44. cur = elem.nodeType === 1 && ( elem.className ?
  45. ( " " + elem.className + " " ).replace( rclass, " " ) :
  46. " "
  47. );
  48. if ( cur ) {
  49. j = 0;
  50. while ( (clazz = classes[j++]) ) {
  51. if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
  52. cur += clazz + " ";
  53. }
  54. }
  55. elem.className = jQuery.trim( cur );
  56. }
  57. }
  58. }
  59. return this;
  60. },
  61. removeClass: function( value ) {
  62. var classes, elem, cur, clazz, j,
  63. i = 0,
  64. len = this.length,
  65. proceed = arguments.length === 0 || typeof value === "string" && value;
  66. if ( jQuery.isFunction( value ) ) {
  67. return this.each(function( j ) {
  68. jQuery( this ).removeClass( value.call( this, j, this.className ) );
  69. });
  70. }
  71. if ( proceed ) {
  72. classes = ( value || "" ).match( core_rnotwhite ) || [];
  73. for ( ; i < len; i++ ) {
  74. elem = this[ i ];
  75. // This expression is here for better compressibility (see addClass)
  76. cur = elem.nodeType === 1 && ( elem.className ?
  77. ( " " + elem.className + " " ).replace( rclass, " " ) :
  78. ""
  79. );
  80. if ( cur ) {
  81. j = 0;
  82. while ( (clazz = classes[j++]) ) {
  83. // Remove *all* instances
  84. while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
  85. cur = cur.replace( " " + clazz + " ", " " );
  86. }
  87. }
  88. elem.className = value ? jQuery.trim( cur ) : "";
  89. }
  90. }
  91. }
  92. return this;
  93. },
  94. toggleClass: function( value, stateVal ) {
  95. var type = typeof value,
  96. isBool = typeof stateVal === "boolean";
  97. if ( jQuery.isFunction( value ) ) {
  98. return this.each(function( i ) {
  99. jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
  100. });
  101. }
  102. return this.each(function() {
  103. if ( type === "string" ) {
  104. // toggle individual class names
  105. var className,
  106. i = 0,
  107. self = jQuery( this ),
  108. state = stateVal,
  109. classNames = value.match( core_rnotwhite ) || [];
  110. while ( (className = classNames[ i++ ]) ) {
  111. // check each className given, space separated list
  112. state = isBool ? state : !self.hasClass( className );
  113. self[ state ? "addClass" : "removeClass" ]( className );
  114. }
  115. // Toggle whole class name
  116. } else if ( type === core_strundefined || type === "boolean" ) {
  117. if ( this.className ) {
  118. // store className if set
  119. jQuery._data( this, "__className__", this.className );
  120. }
  121. // If the element has a class name or if we're passed "false",
  122. // then remove the whole classname (if there was one, the above saved it).
  123. // Otherwise bring back whatever was previously saved (if anything),
  124. // falling back to the empty string if nothing was stored.
  125. this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
  126. }
  127. });
  128. },
  129. hasClass: function( selector ) {
  130. var className = " " + selector + " ",
  131. i = 0,
  132. l = this.length;
  133. for ( ; i < l; i++ ) {
  134. if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
  135. return true;
  136. }
  137. }
  138. return false;
  139. },
  140. val: function( value ) {
  141. var hooks, ret, isFunction,
  142. elem = this[0];
  143. if ( !arguments.length ) {
  144. if ( elem ) {
  145. hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
  146. if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
  147. return ret;
  148. }
  149. ret = elem.value;
  150. return typeof ret === "string" ?
  151. // handle most common string cases
  152. ret.replace(rreturn, "") :
  153. // handle cases where value is null/undef or number
  154. ret == null ? "" : ret;
  155. }
  156. return;
  157. }
  158. isFunction = jQuery.isFunction( value );
  159. return this.each(function( i ) {
  160. var val,
  161. self = jQuery(this);
  162. if ( this.nodeType !== 1 ) {
  163. return;
  164. }
  165. if ( isFunction ) {
  166. val = value.call( this, i, self.val() );
  167. } else {
  168. val = value;
  169. }
  170. // Treat null/undefined as ""; convert numbers to string
  171. if ( val == null ) {
  172. val = "";
  173. } else if ( typeof val === "number" ) {
  174. val += "";
  175. } else if ( jQuery.isArray( val ) ) {
  176. val = jQuery.map(val, function ( value ) {
  177. return value == null ? "" : value + "";
  178. });
  179. }
  180. hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
  181. // If set returns undefined, fall back to normal setting
  182. if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
  183. this.value = val;
  184. }
  185. });
  186. }
  187. });
  188. jQuery.extend({
  189. valHooks: {
  190. option: {
  191. get: function( elem ) {
  192. // attributes.value is undefined in Blackberry 4.7 but
  193. // uses .value. See #6932
  194. var val = elem.attributes.value;
  195. return !val || val.specified ? elem.value : elem.text;
  196. }
  197. },
  198. select: {
  199. get: function( elem ) {
  200. var value, option,
  201. options = elem.options,
  202. index = elem.selectedIndex,
  203. one = elem.type === "select-one" || index < 0,
  204. values = one ? null : [],
  205. max = one ? index + 1 : options.length,
  206. i = index < 0 ?
  207. max :
  208. one ? index : 0;
  209. // Loop through all the selected options
  210. for ( ; i < max; i++ ) {
  211. option = options[ i ];
  212. // oldIE doesn't update selected after form reset (#2551)
  213. if ( ( option.selected || i === index ) &&
  214. // Don't return options that are disabled or in a disabled optgroup
  215. ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
  216. ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
  217. // Get the specific value for the option
  218. value = jQuery( option ).val();
  219. // We don't need an array for one selects
  220. if ( one ) {
  221. return value;
  222. }
  223. // Multi-Selects return an array
  224. values.push( value );
  225. }
  226. }
  227. return values;
  228. },
  229. set: function( elem, value ) {
  230. var values = jQuery.makeArray( value );
  231. jQuery(elem).find("option").each(function() {
  232. this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
  233. });
  234. if ( !values.length ) {
  235. elem.selectedIndex = -1;
  236. }
  237. return values;
  238. }
  239. }
  240. },
  241. attr: function( elem, name, value ) {
  242. var ret, hooks, notxml,
  243. nType = elem.nodeType;
  244. // don't get/set attributes on text, comment and attribute nodes
  245. if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
  246. return;
  247. }
  248. // Fallback to prop when attributes are not supported
  249. if ( typeof elem.getAttribute === core_strundefined ) {
  250. return jQuery.prop( elem, name, value );
  251. }
  252. notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
  253. // All attributes are lowercase
  254. // Grab necessary hook if one is defined
  255. if ( notxml ) {
  256. name = name.toLowerCase();
  257. hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
  258. }
  259. if ( value !== undefined ) {
  260. if ( value === null ) {
  261. jQuery.removeAttr( elem, name );
  262. } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
  263. return ret;
  264. } else {
  265. elem.setAttribute( name, value + "" );
  266. return value;
  267. }
  268. } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
  269. return ret;
  270. } else {
  271. // In IE9+, Flash objects don't have .getAttribute (#12945)
  272. // Support: IE9+
  273. if ( typeof elem.getAttribute !== core_strundefined ) {
  274. ret = elem.getAttribute( name );
  275. }
  276. // Non-existent attributes return null, we normalize to undefined
  277. return ret == null ?
  278. undefined :
  279. ret;
  280. }
  281. },
  282. removeAttr: function( elem, value ) {
  283. var name, propName,
  284. i = 0,
  285. attrNames = value && value.match( core_rnotwhite );
  286. if ( attrNames && elem.nodeType === 1 ) {
  287. while ( (name = attrNames[i++]) ) {
  288. propName = jQuery.propFix[ name ] || name;
  289. // Boolean attributes get special treatment (#10870)
  290. // Set corresponding property to false for boolean attributes
  291. if ( rboolean.test( name ) ) {
  292. elem[ propName ] = false;
  293. }
  294. elem.removeAttribute( name );
  295. }
  296. }
  297. },
  298. attrHooks: {
  299. type: {
  300. set: function( elem, value ) {
  301. if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
  302. // Setting the type on a radio button after the value resets the value in IE6-9
  303. // Reset value to default in case type is set after value during creation
  304. var val = elem.value;
  305. elem.setAttribute( "type", value );
  306. if ( val ) {
  307. elem.value = val;
  308. }
  309. return value;
  310. }
  311. }
  312. }
  313. },
  314. propFix: {
  315. tabindex: "tabIndex",
  316. readonly: "readOnly",
  317. "for": "htmlFor",
  318. "class": "className",
  319. maxlength: "maxLength",
  320. cellspacing: "cellSpacing",
  321. cellpadding: "cellPadding",
  322. rowspan: "rowSpan",
  323. colspan: "colSpan",
  324. usemap: "useMap",
  325. frameborder: "frameBorder",
  326. contenteditable: "contentEditable"
  327. },
  328. prop: function( elem, name, value ) {
  329. var ret, hooks, notxml,
  330. nType = elem.nodeType;
  331. // don't get/set properties on text, comment and attribute nodes
  332. if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
  333. return;
  334. }
  335. notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
  336. if ( notxml ) {
  337. // Fix name and attach hooks
  338. name = jQuery.propFix[ name ] || name;
  339. hooks = jQuery.propHooks[ name ];
  340. }
  341. if ( value !== undefined ) {
  342. if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
  343. return ret;
  344. } else {
  345. return ( elem[ name ] = value );
  346. }
  347. } else {
  348. if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
  349. return ret;
  350. } else {
  351. return elem[ name ];
  352. }
  353. }
  354. },
  355. propHooks: {
  356. tabIndex: {
  357. get: function( elem ) {
  358. // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
  359. // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
  360. var attributeNode = elem.getAttributeNode("tabindex");
  361. return attributeNode && attributeNode.specified ?
  362. parseInt( attributeNode.value, 10 ) :
  363. rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
  364. 0 :
  365. undefined;
  366. }
  367. }
  368. }
  369. });
  370. // Hook for boolean attributes
  371. boolHook = {
  372. get: function( elem, name ) {
  373. return elem.getAttribute( name ) !== null ?
  374. name.toLowerCase() :
  375. undefined;
  376. },
  377. set: function( elem, value, name ) {
  378. if ( value === false ) {
  379. // Remove boolean attributes when set to false
  380. jQuery.removeAttr( elem, name );
  381. } else {
  382. elem.setAttribute( name, name );
  383. }
  384. return name;
  385. }
  386. };
  387. // Radios and checkboxes getter/setter
  388. if ( !jQuery.support.checkOn ) {
  389. jQuery.each([ "radio", "checkbox" ], function() {
  390. jQuery.valHooks[ this ] = {
  391. get: function( elem ) {
  392. // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
  393. return elem.getAttribute("value") === null ? "on" : elem.value;
  394. }
  395. };
  396. });
  397. }
  398. jQuery.each([ "radio", "checkbox" ], function() {
  399. jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
  400. set: function( elem, value ) {
  401. if ( jQuery.isArray( value ) ) {
  402. return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
  403. }
  404. }
  405. });
  406. });
  407. // IE9/10 do not see a selected option inside an optgroup unless you access it
  408. // Support: IE9, IE10
  409. if ( !jQuery.support.optSelected ) {
  410. jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
  411. get: function( elem ) {
  412. var parent = elem.parentNode;
  413. if ( parent && parent.parentNode ) {
  414. parent.parentNode.selectedIndex;
  415. }
  416. return null;
  417. }
  418. });
  419. }