diff --git a/README.md b/README.md index d56576c8..17b38de6 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,89 @@ Sometimes, the various git repositories get into an inconsistent state where bui (usually this results in the jquery.js or jquery.min.js being 0 bytes). If this happens, run `make clean`, then run `make` again. +Git for dummies +--------------- + +As the source code is handled by the version control system Git, it's useful to know some features used. + +### Submodules ### + +The repository uses submodules, which normally are handles directly by the Makefile, but sometimes you want to +be able to work with them manually. + +Following are the steps to manually get the submodules: + +1. `git clone https://github.com/jquery/jquery.git` +2. `git submodule init` +3. `git submodule update` + +Or: + +1. `git clone https://github.com/jquery/jquery.git` +2. `git submodule update --init` + +Or: + +1. `git clone --recursive https://github.com/jquery/jquery.git` + +If you want to work inside a submodule, it is possible, but first you need to checkout a branch: + +1. `cd src/sizzle` +2. `git checkout master` + +After you've commited your changes to the submodule, you'll update the jquery project to point to the new commit, +but remember to push the submodule changes before pushing the new jquery commit: + +1. `cd src/sizzle` +2. `git push origin master` +3. `cd ..` +4. `git add src/sizzle` +5. `git commit` + +The makefile has some targets to simplify submodule handling: + +#### `make update_submodules` #### + +checks out the commit pointed to byu jquery, but merges your local changes, if any. This target is executed +when you are doing a normal `make`. + +#### `make pull_submodules` #### + +updates the content of the submoduels to what is probably the latest upstream code + +#### `make pull` #### + +make a `make pull_submodules` and after that a `git pull`. if you have no remote tracking in your master branch, you can +execute this command as `make pull REMOTE=origin BRANCH=master` instead. + +### cleaning ### + +If you want to purge your working directory back to the status of upstream, following commands can be used (remember everything you've worked on is gone after these): + +1. `git reset --hard upstream/master` +2. `git clean -fdx` + +### rebasing ### + +For feature/topic branches, you should always used the `--rebase` flag to `git pull`, or if you are usually handling many temporary "to be in a github pull request" branches, run following to automate this: + +* `git config branch.autosetuprebase local` (see `man git-config` for more information) + +### handling merge conflicts ### + +If you're getting merge conflicts when merging, instead of editing the conflicted files manually, you can use the feature +`git mergetool`. Even though the default tool `xxdiff` looks awful/old, it's rather useful. + +Following are some commands that can be used there: + +* `Ctrl + Alt + M` - automerge as much as possible +* `b` - jump to next merge conflict +* `s` - change the order of the conflicted lines +* `u` - undo an merge +* `left mouse button` - mark a block to be the winner +* `middle mouse button` - mark a line to be the winner +* `Ctrl + S` - save +* `Ctrl + Q` - quit Questions? ---------- diff --git a/src/attributes.js b/src/attributes.js index 59972105..c34cd619 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -3,44 +3,41 @@ var rclass = /[\n\t\r]/g, rspaces = /\s+/, rreturn = /\r/g, - rspecialurl = /^(?:href|src|style)$/, rtype = /^(?:button|input)$/i, rfocusable = /^(?:button|input|object|select|textarea)$/i, rclickable = /^a(?:rea)?$/i, - rradiocheck = /^(?:radio|checkbox)$/i; - -jQuery.props = { - "for": "htmlFor", - "class": "className", - readonly: "readOnly", - maxlength: "maxLength", - cellspacing: "cellSpacing", - rowspan: "rowSpan", - colspan: "colSpan", - tabindex: "tabIndex", - usemap: "useMap", - frameborder: "frameBorder" -}; + formHook; jQuery.fn.extend({ attr: function( name, value ) { return jQuery.access( this, name, value, true, jQuery.attr ); }, - removeAttr: function( name, fn ) { - return this.each(function(){ - jQuery.attr( this, name, "" ); - if ( this.nodeType === 1 ) { - this.removeAttribute( name ); - } + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} }); }, addClass: function( value ) { - if ( jQuery.isFunction(value) ) { + if ( jQuery.isFunction( value ) ) { return this.each(function(i) { var self = jQuery(this); - self.addClass( value.call(this, i, self.attr("class")) ); + self.addClass( value.call(this, i, self.attr("class") || "") ); }); } @@ -154,82 +151,36 @@ jQuery.fn.extend({ }, val: function( value ) { + var hooks, ret, + elem = this[0]; + if ( !arguments.length ) { - var elem = this[0]; - if ( elem ) { - if ( jQuery.nodeName( elem, "option" ) ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem )) !== undefined ) { + return ret; } - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { - - // Get the specific value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - - return values; - } - - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { - return elem.getAttribute("value") === null ? "on" : elem.value; - } - - // Everything else, we just grab the value return (elem.value || "").replace(rreturn, ""); - } return undefined; } - var isFunction = jQuery.isFunction(value); + var isFunction = jQuery.isFunction( value ); - return this.each(function(i) { - var self = jQuery(this), val = value; + return this.each(function( i ) { + var self = jQuery(this), val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { - val = value.call(this, i, self.val()); + val = value.call( this, i, self.val() ); + } else { + val = value; } // Treat null/undefined as ""; convert numbers to string @@ -237,27 +188,16 @@ jQuery.fn.extend({ val = ""; } else if ( typeof val === "number" ) { val += ""; - } else if ( jQuery.isArray(val) ) { - val = jQuery.map(val, function (value) { + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { return value == null ? "" : value + ""; }); } - if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { - this.checked = jQuery.inArray( self.val(), val ) >= 0; + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; - } else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(val); - - jQuery( "option", this ).each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - this.selectedIndex = -1; - } - - } else { + // If set returns undefined, fall back to normal setting + if ( !hooks || ("set" in hooks && hooks.set( this, val ) === undefined) ) { this.value = val; } }); @@ -265,6 +205,71 @@ jQuery.fn.extend({ }); jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + attrFn: { val: true, css: true, @@ -275,115 +280,266 @@ jQuery.extend({ height: true, offset: true }, - + + attrFix: { + // Always normalize to ensure hook usage + tabindex: "tabIndex", + readonly: "readOnly" + }, + attr: function( elem, name, value, pass ) { + var nType = elem.nodeType; + // don't get/set attributes on text, comment and attribute nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) { + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return undefined; } if ( pass && name in jQuery.attrFn ) { - return jQuery(elem)[name](value); + return jQuery( elem )[ name ]( value ); } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Normalize the name if needed + name = notxml && jQuery.attrFix[ name ] || name; - var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), - // Whether we are setting (or getting) - set = value !== undefined; + // Get the appropriate hook, or the formHook + // if getSetAttribute is not supported and we have form objects in IE6/7 + hooks = jQuery.attrHooks[ name ] || ( elem.nodeName === "FORM" && formHook ); - // Try to normalize/fix the name - name = notxml && jQuery.props[ name ] || name; + if ( value !== undefined ) { - // Only do all the following if this is a node (faster for style) - if ( elem.nodeType === 1 ) { - // These attributes require special treatment - var special = rspecialurl.test( name ); + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; - // Safari mis-reports the default selected property of an option - // Accessing the parent's selectedIndex property fixes it - if ( name === "selected" && !jQuery.support.optSelected ) { - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } + } else { + elem.setAttribute( name, "" + value ); + return value; } - // If applicable, access the attribute via the DOM 0 way - // 'in' checks fail in Blackberry 4.7 #6931 - if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) { - if ( set ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } + } else { - if ( value === null ) { - if ( elem.nodeType === 1 ) { - elem.removeAttribute( name ); - } + if ( hooks && "get" in hooks && notxml ) { + return hooks.get( elem, name ); - } else { - elem[ name ] = value; - } + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + } + }, + + removeAttr: function( elem, name ) { + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); } - - // browsers index elements by id/name on forms, give priority to attributes. - if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { - return elem.getAttributeNode( name ).nodeValue; - } - + } + }, + tabIndex: { + get: function( elem ) { // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - if ( name === "tabIndex" ) { - var attributeNode = elem.getAttributeNode( "tabIndex" ); - - return attributeNode && attributeNode.specified ? - attributeNode.value : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } + var attributeNode = elem.getAttributeNode("tabIndex"); + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + }, + + propFix: {}, + + prop: function( elem, name, value ) { + var nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Try to normalize/fix the name + name = notxml && jQuery.propFix[ name ] || name; + + hooks = jQuery.propHooks[ name ]; + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return (elem[ name ] = value); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) { + return ret; + + } else { return elem[ name ]; } + } + }, + + propHooks: {} +}); - if ( !jQuery.support.style && notxml && name === "style" ) { - if ( set ) { - elem.style.cssText = "" + value; +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !jQuery.support.getSetAttribute ) { + jQuery.attrFix = jQuery.extend( jQuery.attrFix, { + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder" + }); + + // Use this for any attribute on a form in IE6/7 + // And the name attribute + formHook = jQuery.attrHooks.name = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + // Return undefined if not specified instead of empty string + return ret && ret.specified ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return value; + } + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; } - - return elem.style.cssText; } + }); + }); +} - if ( set ) { - // convert the value to a string (all browsers do this but IE) see #1070 - elem.setAttribute( name, "" + value ); +// Remove certain attrs if set to false +jQuery.each([ "selected", "checked", "readOnly", "disabled" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === false ) { + jQuery.removeAttr( elem, name ); + return value; } - - // Ensure that missing attributes return undefined - // Blackberry 4.7 returns "" from getAttribute #6938 - if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) { - return undefined; - } - - var attr = !jQuery.support.hrefNormalized && notxml && special ? - // Some attributes require a special call on IE - elem.getAttribute( name, 2 ) : - elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return attr === null ? undefined : attr; } - // Handle everything which isn't a DOM element node - if ( set ) { - elem[ name ] = value; + }); +}); + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height", "list" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return (elem.style.cssText = "" + value); } - return elem[ name ]; - } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }); +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + } + } + }); }); })( jQuery ); diff --git a/src/core.js b/src/core.js index 3331969a..d3641365 100644 --- a/src/core.js +++ b/src/core.js @@ -88,7 +88,7 @@ jQuery.fn = jQuery.prototype = { if ( selector === "body" && !context && document.body ) { this.context = document; this[0] = document.body; - this.selector = "body"; + this.selector = selector; this.length = 1; return this; } @@ -658,8 +658,9 @@ jQuery.extend({ }, inArray: function( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); + + if ( indexOf ) { + return indexOf.call( array, elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { @@ -709,15 +710,30 @@ jQuery.extend({ // arg is for internal usage only map: function( elems, callback, arg ) { - var ret = [], value; + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || jQuery.isArray( elems ) ) ; // Go through the array, translating each of the items to their - // new value (or values). - for ( var i = 0, length = elems.length; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); - if ( value != null ) { - ret[ ret.length ] = value; + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go thorugh every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } } } @@ -728,31 +744,52 @@ jQuery.extend({ // A global GUID counter for objects guid: 1, - proxy: function( fn, proxy, thisObject ) { - if ( arguments.length === 2 ) { - if ( typeof proxy === "string" ) { - thisObject = fn; - fn = thisObject[ proxy ]; - proxy = undefined; + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy; - } else if ( proxy && !jQuery.isFunction( proxy ) ) { - thisObject = proxy; - proxy = undefined; + // XXX BACKCOMPAT: Support old string method. + if ( typeof context === "string" ) { + fn = fn[ context ]; + context = arguments[0]; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( ! jQuery.isFunction( fn ) ) { + return undefined; + } + + if ( jQuery.support.nativeBind ) { + // Native bind + args = slice.call( arguments, 1 ); + if ( args.length ) { + proxy = Function.prototype.bind.apply( fn, args ); + } else { + proxy = fn.bind( context ); + } + } else { + // Simulated bind + args = slice.call( arguments, 2 ); + if ( args.length ) { + proxy = function() { + return arguments.length ? + fn.apply( context, args.concat( slice.call( arguments ) ) ) : + fn.apply( context, args ); + }; + } else { + proxy = function() { + return arguments.length ? + fn.apply( context, arguments ) : + fn.call( context ); + }; } } - if ( !proxy && fn ) { - proxy = function() { - return fn.apply( thisObject || this, arguments ); - }; - } - // Set the guid of unique handler to the same of original handler, so it can be removed - if ( fn ) { - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - } + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - // So proxy can be declared as an argument return proxy; }, @@ -843,12 +880,6 @@ if ( jQuery.browser.webkit ) { jQuery.browser.safari = true; } -if ( indexOf ) { - jQuery.inArray = function( elem, array ) { - return indexOf.call( array, elem ); - }; -} - // IE doesn't match non-breaking spaces with \s if ( rnotwhite.test( "\xA0" ) ) { trimLeft = /^[\s\xA0]+/; diff --git a/src/data.js b/src/data.js index 2d53a710..c2fd558f 100644 --- a/src/data.js +++ b/src/data.js @@ -1,6 +1,7 @@ (function( jQuery ) { -var rbrace = /^(?:\{.*\}|\[.*\])$/; +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([a-z])([A-Z])/g; jQuery.extend({ cache: {}, @@ -223,12 +224,13 @@ jQuery.fn.extend({ data = jQuery.data( this[0] ); if ( this[0].nodeType === 1 ) { - var attr = this[0].attributes, name; + var attr = this[0].attributes, name; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name; if ( name.indexOf( "data-" ) === 0 ) { - name = name.substr( 5 ); + name = jQuery.camelCase( name.substring(5) ); + dataAttr( this[0], name, data[ name ] ); } } @@ -282,7 +284,9 @@ function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { - data = elem.getAttribute( "data-" + key ); + name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + + data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { diff --git a/src/effects.js b/src/effects.js index d6eff7b3..7aec8300 100644 --- a/src/effects.js +++ b/src/effects.js @@ -12,7 +12,10 @@ var elemdisplay = {}, // opacity animations [ "opacity" ] ], - fxNow; + fxNow, + requestAnimationFrame = window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame; function clearFxNow() { fxNow = undefined; @@ -368,7 +371,8 @@ jQuery.fx.prototype = { // Start an animation from one number to another custom: function( from, to, unit ) { var self = this, - fx = jQuery.fx; + fx = jQuery.fx, + raf; this.startTime = fxNow || createFxNow(); this.start = from; @@ -384,7 +388,20 @@ jQuery.fx.prototype = { t.elem = this.elem; if ( t() && jQuery.timers.push(t) && !timerId ) { - timerId = setInterval(fx.tick, fx.interval); + // Use requestAnimationFrame instead of setInterval if available + if ( requestAnimationFrame ) { + timerId = 1; + raf = function() { + // When timerId gets set to null at any point, this stops + if ( timerId ) { + requestAnimationFrame( raf ); + fx.tick(); + } + }; + requestAnimationFrame( raf ); + } else { + timerId = setInterval( fx.tick, fx.interval ); + } } }, diff --git a/src/event.js b/src/event.js index 788bb20e..ac0da6b7 100644 --- a/src/event.js +++ b/src/event.js @@ -1,6 +1,7 @@ (function( jQuery ) { -var rnamespaces = /\.(.*)$/, +var hasOwn = Object.prototype.hasOwnProperty, + rnamespaces = /\.(.*)$/, rformElems = /^(?:textarea|input|select)$/i, rperiod = /\./g, rspace = / /g, @@ -563,7 +564,15 @@ jQuery.Event = function( src ) { // Event object if ( src && src.type ) { this.originalEvent = src; - this.type = src.type; + + // Push explicitly provided properties onto the event object + for ( var prop in src ) { + // Ensure we don't clobber jQuery.Event prototype + // with own properties. + if ( hasOwn.call( src, prop ) ) { + this[ prop ] = src[ prop ]; + } + } // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. @@ -852,10 +861,10 @@ function trigger( type, elem, args ) { // Create "bubbling" focus and blur events if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - + // Attach a single capturing handler while someone wants focusin/focusout var attaches = 0; - + jQuery.event.special[ fix ] = { setup: function() { if ( attaches++ === 0 ) { @@ -885,6 +894,8 @@ if ( !jQuery.support.focusinBubbles ) { jQuery.each(["bind", "one"], function( i, name ) { jQuery.fn[ name ] = function( type, data, fn ) { + var handler; + // Handle object literals if ( typeof type === "object" ) { for ( var key in type ) { @@ -898,10 +909,15 @@ jQuery.each(["bind", "one"], function( i, name ) { data = undefined; } - var handler = name === "one" ? jQuery.proxy( fn, function( event ) { - jQuery( this ).unbind( event, handler ); - return fn.apply( this, arguments ); - }) : fn; + if ( name === "one" ) { + handler = function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }; + handler.guid = fn.guid || jQuery.guid++; + } else { + handler = fn; + } if ( type === "unload" && name !== "one" ) { this.one( type, data, fn ); @@ -965,24 +981,27 @@ jQuery.fn.extend({ toggle: function( fn ) { // Save reference to arguments for access in closure var args = arguments, - i = 1; + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; while ( i < args.length ) { - jQuery.proxy( fn, args[ i++ ] ); + args[ i++ ].guid = guid; } - return this.click( jQuery.proxy( fn, function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - })); + return this.click( toggler ); }, hover: function( fnOver, fnOut ) { @@ -1168,3 +1187,4 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl }); })( jQuery ); + diff --git a/src/manipulation.js b/src/manipulation.js index 27f81cc2..758cdbae 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -377,7 +377,7 @@ function cloneCopyEvent( src, dest ) { } } -function cloneFixAttributes(src, dest) { +function cloneFixAttributes( src, dest ) { // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { return; @@ -549,7 +549,8 @@ jQuery.extend({ // Return the cloned set return clone; -}, + }, + clean: function( elems, context, fragment, scripts ) { context = context || document; diff --git a/src/sizzle b/src/sizzle index f12b9309..c50b0cdd 160000 --- a/src/sizzle +++ b/src/sizzle @@ -1 +1 @@ -Subproject commit f12b9309269ba7e705a99efe099f86ed1fe98d58 +Subproject commit c50b0cddec48494ba882606d03e14cce647d243b diff --git a/src/support.js b/src/support.js index 50ae6dfd..34960505 100644 --- a/src/support.js +++ b/src/support.js @@ -19,7 +19,8 @@ jQuery.support = (function() { isSupported; // Preliminary tests - div.innerHTML = "
a"; + div.setAttribute("className", "t"); + div.innerHTML = "
a"; all = div.getElementsByTagName( "*" ); a = div.getElementsByTagName( "a" )[ 0 ]; @@ -48,7 +49,7 @@ jQuery.support = (function() { // Get the style information from getAttribute // (IE uses .cssText instead) - style: /red/.test( a.getAttribute("style") ), + style: /top/.test( a.getAttribute("style") ), // Make sure that URLs aren't manipulated // (IE normalizes it by default) @@ -71,6 +72,13 @@ jQuery.support = (function() { // Make sure that a selected-by-default option has a working selected property. // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Test for presence of native Function#bind. + // Not in: >= Chrome 6, >= FireFox 3, Safari 5?, IE 9?, Opera 11? + nativeBind: jQuery.isFunction( Function.prototype.bind ), // Will be defined later submitBubbles: true, diff --git a/src/traversing.js b/src/traversing.js index fe2e33d8..fb5946bb 100644 --- a/src/traversing.js +++ b/src/traversing.js @@ -17,17 +17,30 @@ var runtil = /Until$/, jQuery.fn.extend({ find: function( selector ) { - var ret = this.pushStack( "", "find", selector ), - length = 0; + var self = this, + i, l; - for ( var i = 0, l = this.length; i < l; i++ ) { + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { length = ret.length; jQuery.find( selector, this[i], ret ); if ( i > 0 ) { // Make sure that the results are unique - for ( var n = length; n < ret.length; n++ ) { - for ( var r = 0; r < length; r++ ) { + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { if ( ret[r] === ret[n] ) { ret.splice(n--, 1); break; @@ -60,12 +73,15 @@ jQuery.fn.extend({ }, is: function( selector ) { - return !!selector && jQuery.filter( selector, this ).length > 0; + return !!selector && (typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0); }, closest: function( selectors, context ) { var ret = [], i, l, cur = this[0]; - + + // Array if ( jQuery.isArray( selectors ) ) { var match, selector, matches = {}, @@ -75,8 +91,8 @@ jQuery.fn.extend({ for ( i = 0, l = selectors.length; i < l; i++ ) { selector = selectors[i]; - if ( !matches[selector] ) { - matches[selector] = jQuery.expr.match.POS.test( selector ) ? + if ( !matches[ selector ] ) { + matches[ selector ] = POS.test( selector ) ? jQuery( selector, context || this.context ) : selector; } @@ -84,9 +100,9 @@ jQuery.fn.extend({ while ( cur && cur.ownerDocument && cur !== context ) { for ( selector in matches ) { - match = matches[selector]; + match = matches[ selector ]; - if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { + if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { ret.push({ selector: selector, elem: cur, level: level }); } } @@ -99,8 +115,10 @@ jQuery.fn.extend({ return ret; } - var pos = POS.test( selectors ) ? - jQuery( selectors, context || this.context ) : null; + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; for ( i = 0, l = this.length; i < l; i++ ) { cur = this[i]; @@ -112,14 +130,14 @@ jQuery.fn.extend({ } else { cur = cur.parentNode; - if ( !cur || !cur.ownerDocument || cur === context ) { + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { break; } } } } - ret = ret.length > 1 ? jQuery.unique(ret) : ret; + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; return this.pushStack( ret, "closest", selectors ); }, @@ -286,7 +304,7 @@ function winnow( elements, qualifier, keep ) { return retVal === keep; }); - } else if ( qualifier.nodeType ) { + } else if ( qualifier && qualifier.nodeType ) { return jQuery.grep(elements, function( elem, i ) { return (elem === qualifier) === keep; }); diff --git a/test/index.html b/test/index.html index c7c2ae55..a1065508 100644 --- a/test/index.html +++ b/test/index.html @@ -203,6 +203,10 @@ Z + + + +
diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 8cf47bed..f3f0bab2 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -3,38 +3,81 @@ module("attributes", { teardown: moduleTeardown }); var bareObj = function(value) { return value; }; var functionReturningObj = function(value) { return (function() { return value; }); }; -test("jQuery.props: itegrity test", function() { - expect(1); +test("jQuery.attrFix integrity test", function() { + expect(1); + + // This must be maintained and equal jQuery.attrFix when appropriate + // Ensure that accidental or erroneous property + // overwrites don't occur + // This is simply for better code coverage and future proofing. + var propsShouldBe; + if ( !jQuery.support.getSetAttribute ) { + propsShouldBe = { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder" + }; + } else { + propsShouldBe = { + tabindex: "tabIndex", + readonly: "readOnly" + }; + } - // This must be maintained and equal jQuery.props - // Ensure that accidental or erroneous property - // overwrites don't occur - // This is simply for better code coverage and future proofing. - var propsShouldBe = { - "for": "htmlFor", - "class": "className", - readonly: "readOnly", - maxlength: "maxLength", - cellspacing: "cellSpacing", - rowspan: "rowSpan", - colspan: "colSpan", - tabindex: "tabIndex", - usemap: "useMap", - frameborder: "frameBorder" - }; - - same(propsShouldBe, jQuery.props, "jQuery.props passes integrity check"); + same(propsShouldBe, jQuery.attrFix, "jQuery.attrFix passes integrity check"); +}); +test("prop(String, Object)", function() { + expect(19); + equals( jQuery('#text1').prop('value'), "Test", 'Check for value attribute' ); + equals( jQuery('#text1').prop('value', "Test2").prop('defaultValue'), "Test", 'Check for defaultValue attribute' ); + equals( jQuery('#select2').prop('selectedIndex'), 3, 'Check for selectedIndex attribute' ); + equals( jQuery('#foo').prop('nodeName').toUpperCase(), 'DIV', 'Check for nodeName attribute' ); + equals( jQuery('#foo').prop('tagName').toUpperCase(), 'DIV', 'Check for tagName attribute' ); + equals( jQuery("