Fixed bubbling of live events (if an inner element handles an event first - and stops...
[jquery.git] / src / core.js
index e2f1f0b..ac8ec6b 100644 (file)
@@ -47,20 +47,16 @@ jQuery.fn = jQuery.prototype = {
                                else {
                                        var elem = document.getElementById( match[3] );
 
-                                       // Make sure an element was located
-                                       if ( elem ){
-                                               // Handle the case where IE and Opera return items
-                                               // by name instead of ID
-                                               if ( elem.id != match[3] )
-                                                       return jQuery().find( selector );
-
-                                               // Otherwise, we inject the element directly into the jQuery object
-                                               var ret = jQuery( elem );
-                                               ret.context = document;
-                                               ret.selector = selector;
-                                               return ret;
-                                       }
-                                       selector = [];
+                                       // Handle the case where IE and Opera return items
+                                       // by name instead of ID
+                                       if ( elem && elem.id != match[3] )
+                                               return jQuery().find( selector );
+
+                                       // Otherwise, we inject the element directly into the jQuery object
+                                       var ret = jQuery( elem || [] );
+                                       ret.context = document;
+                                       ret.selector = selector;
+                                       return ret;
                                }
 
                        // HANDLE: $(expr, [context])
@@ -73,6 +69,12 @@ jQuery.fn = jQuery.prototype = {
                } else if ( jQuery.isFunction( selector ) )
                        return jQuery( document ).ready( selector );
 
+               // Make sure that old selector state is passed along
+               if ( selector.selector && selector.context ) {
+                       this.selector = selector.selector;
+                       this.context = selector.context;
+               }
+
                return this.setArray(jQuery.makeArray(selector));
        },
 
@@ -200,20 +202,22 @@ jQuery.fn = jQuery.prototype = {
        },
 
        wrapAll: function( html ) {
-               if ( this[0] )
+               if ( this[0] ) {
                        // The elements to wrap the target around
-                       jQuery( html, this[0].ownerDocument )
-                               .clone()
-                               .insertBefore( this[0] )
-                               .map(function(){
-                                       var elem = this;
+                       var wrap = jQuery( html, this[0].ownerDocument ).clone();
+
+                       if ( this[0].parentNode )
+                               wrap.insertBefore( this[0] );
 
-                                       while ( elem.firstChild )
-                                               elem = elem.firstChild;
+                       wrap.map(function(){
+                               var elem = this;
 
-                                       return elem;
-                               })
-                               .append(this);
+                               while ( elem.firstChild )
+                                       elem = elem.firstChild;
+
+                               return elem;
+                       }).append(this);
+               }
 
                return this;
        },
@@ -260,9 +264,16 @@ jQuery.fn = jQuery.prototype = {
                return this.prevObject || jQuery( [] );
        },
 
+       // For internal use only.
+       // Behaves like an Array's .push method, not like a jQuery method.
+       push: [].push,
+
        find: function( selector ) {
-               if ( this.length === 1 ) {
-                       return this.pushStack( jQuery.find( selector, this[0] ), "find", selector );
+               if ( this.length === 1 && !/,/.test(selector) ) {
+                       var ret = this.pushStack( [], "find", selector );
+                       ret.length = 0;
+                       jQuery.find( selector, this[0], ret );
+                       return ret;
                } else {
                        var elems = jQuery.map(this, function(elem){
                                return jQuery.find( selector, elem );
@@ -286,33 +297,37 @@ jQuery.fn = jQuery.prototype = {
                                // attributes in IE that are actually only stored
                                // as properties will not be copied (such as the
                                // the name attribute on an input).
-                               var clone = this.cloneNode(true),
-                                       container = document.createElement("div");
-                               container.appendChild(clone);
-                               return jQuery.clean([container.innerHTML])[0];
+                               var html = this.outerHTML;
+                               if ( !html ) {
+                                       var div = this.ownerDocument.createElement("div");
+                                       div.appendChild( this.cloneNode(true) );
+                                       html = div.innerHTML;
+                               }
+
+                               return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
                        } else
                                return this.cloneNode(true);
                });
 
-               // Need to set the expando to null on the cloned set if it exists
-               // removeData doesn't work here, IE removes it from the original as well
-               // this is primarily for IE but the data expando shouldn't be copied over in any browser
-               var clone = ret.find("*").andSelf().each(function(){
-                       if ( this[ expando ] !== undefined )
-                               this[ expando ] = null;
-               });
-
                // Copy the events from the original to the clone
-               if ( events === true )
-                       this.find("*").andSelf().each(function(i){
-                               if (this.nodeType == 3)
+               if ( events === true ) {
+                       var orig = this.find("*").andSelf(), i = 0;
+
+                       ret.find("*").andSelf().each(function(){
+                               if ( this.nodeName !== orig[i].nodeName )
                                        return;
-                               var events = jQuery.data( this, "events" );
 
-                               for ( var type in events )
-                                       for ( var handler in events[ type ] )
-                                               jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
+                               var events = jQuery.data( orig[i], "events" );
+
+                               for ( var type in events ) {
+                                       for ( var handler in events[ type ] ) {
+                                               jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+                                       }
+                               }
+
+                               i++;
                        });
+               }
 
                // Return the cloned set
                return ret;
@@ -331,12 +346,18 @@ jQuery.fn = jQuery.prototype = {
        },
 
        closest: function( selector ) {
+               var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
+                       closer = 0;
+
                return this.map(function(){
                        var cur = this;
                        while ( cur && cur.ownerDocument ) {
-                               if ( jQuery(cur).is(selector) )
+                               if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+                                       jQuery.data(cur, "closest", closer);
                                        return cur;
+                               }
                                cur = cur.parentNode;
+                               closer++;
                        }
                });
        },
@@ -449,7 +470,7 @@ jQuery.fn = jQuery.prototype = {
        html: function( value ) {
                return value === undefined ?
                        (this[0] ?
-                               this[0].innerHTML :
+                               this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
                                null) :
                        this.empty().append( value );
        },
@@ -604,15 +625,13 @@ jQuery.extend({
 
        // check if an element is in a (or is an) XML document
        isXMLDoc: function( elem ) {
-               return elem.documentElement && !elem.body ||
-                       elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+               return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+                       !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
        },
 
        // Evalulates a script in a global context
        globalEval: function( data ) {
-               data = jQuery.trim( data );
-
-               if ( data ) {
+               if ( data && /\S/.test(data) ) {
                        // Inspired by code by Andrea Giammarchi
                        // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
                        var head = document.getElementsByTagName("head")[0] || document.documentElement,
@@ -695,7 +714,7 @@ jQuery.extend({
 
                // internal only, use hasClass("class")
                has: function( elem, className ) {
-                       return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+                       return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
                }
        },
 
@@ -840,7 +859,7 @@ jQuery.extend({
                                });
 
                                // Trim whitespace, otherwise indexOf won't work as expected
-                               var tags = jQuery.trim( elem ).toLowerCase();
+                               var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
 
                                var wrap =
                                        // option or optgroup
@@ -880,11 +899,12 @@ jQuery.extend({
                                if ( !jQuery.support.tbody ) {
 
                                        // String was a <table>, *may* have spurious <tbody>
-                                       var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
-                                               div.firstChild && div.firstChild.childNodes :
+                                       var hasBody = /<tbody/i.test(elem),
+                                               tbody = !tags.indexOf("<table") && !hasBody ?
+                                                       div.firstChild && div.firstChild.childNodes :
 
                                                // String was a bare <thead> or <tfoot>
-                                               wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
+                                               wrap[1] == "<table>" && !hasBody ?
                                                        div.childNodes :
                                                        [];
 
@@ -910,8 +930,8 @@ jQuery.extend({
 
                if ( fragment ) {
                        for ( var i = 0; ret[i]; i++ ) {
-                               if ( jQuery.nodeName( ret[i], "script" ) ) {
-                                       scripts.push( ret[i].parentNode.removeChild( ret[i] ) );
+                               if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+                                       scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
                                } else {
                                        if ( ret[i].nodeType === 1 )
                                                ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
@@ -946,7 +966,7 @@ jQuery.extend({
 
                        // Safari mis-reports the default selected property of a hidden option
                        // Accessing the parent's selectedIndex property fixes it
-                       if ( name == "selected" )
+                       if ( name == "selected" && elem.parentNode )
                                elem.parentNode.selectedIndex;
 
                        // If applicable, access the attribute via the DOM 0 way
@@ -963,11 +983,17 @@ jQuery.extend({
                                if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
                                        return elem.getAttributeNode( name ).nodeValue;
 
-                               // elem.tabindex doesn't always return the correct value
+                               // 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 == jQuery.props.tabindex ) {
-                                       var attributeNode = elem.getAttributeNode(jQuery.props.tabindex);
-                                       return attributeNode && attributeNode.specified && attributeNode.value || undefined;
+                               if ( name == "tabIndex" ) {
+                                       var attributeNode = elem.getAttributeNode( "tabIndex" );
+                                       return attributeNode && attributeNode.specified
+                                               ? attributeNode.value
+                                               : elem.nodeName.match(/(button|input|object|select|textarea)/i)
+                                                       ? 0
+                                                       : elem.nodeName.match(/^(a|area)$/i) && elem.href
+                                                               ? 0
+                                                               : undefined;
                                }
 
                                return elem[ name ];
@@ -1129,9 +1155,6 @@ jQuery.browser = {
        mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
 };
 
-// Check to see if the W3C box model is being used
-jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat";
-
 jQuery.each({
        parent: function(elem){return elem.parentNode;},
        parents: function(elem){return jQuery.dir(elem,"parentNode");},