jquery-1.4.2 manipulation部分源码分析

manipulation部分源码分析

var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,    rleadingWhitespace = /^\s+/,    rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,    rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,    rtagName = /<([\w:]+)/,    rtbody = /<tbody/i,    rhtml = /<|&#?\w+;/,    rnocache = /<script|<object|<embed|<option|<style/i,    rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)    fcloseTag = function( all, front, tag ) {        return rselfClosing.test( tag ) ?            all :            front + "></" + tag + ">";    },    wrapMap = {        option: [ 1, "<select multiple='multiple'>", "</select>" ],        legend: [ 1, "<fieldset>", "</fieldset>" ],        thead: [ 1, "<table>", "</table>" ],        tr: [ 2, "<table><tbody>", "</tbody></table>" ],        td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],        col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],        area: [ 1, "<map>", "</map>" ],        _default: [ 0, "", "" ]    };wrapMap.optgroup = wrapMap.option;wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;wrapMap.th = wrapMap.td;// IE can't serialize <link> and <script> tags normallyif ( !jQuery.support.htmlSerialize ) {    wrapMap._default = [ 1, "div<div>", "</div>" ];}
jQuery.fn.extend({    text: function( text ) {        if ( jQuery.isFunction(text) ) {            return this.each(function(i) {                var self = jQuery(this);                self.text( text.call(this, i, self.text()) );            });        }        if ( typeof text !== "object" && text !== undefined ) {            return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );        }        return jQuery.text( this );    },    wrapAll: function( html ) {        if ( jQuery.isFunction( html ) ) {            return this.each(function(i) {                jQuery(this).wrapAll( html.call(this, i) );            });        }        if ( this[0] ) {            // The elements to wrap the target around            var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);            if ( this[0].parentNode ) {                wrap.insertBefore( this[0] );            }            wrap.map(function() {                var elem = this;                while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {                    elem = elem.firstChild;                }                return elem;            }).append(this);        }        return this;    },    wrapInner: function( html ) {        if ( jQuery.isFunction( html ) ) {            return this.each(function(i) {                jQuery(this).wrapInner( html.call(this, i) );            });        }        return this.each(function() {            var self = jQuery( this ), contents = self.contents();            if ( contents.length ) {                contents.wrapAll( html );            } else {                self.append( html );            }        });    },    wrap: function( html ) {        return this.each(function() {            jQuery( this ).wrapAll( html );        });    },    unwrap: function() {        return this.parent().each(function() {            if ( !jQuery.nodeName( this, "body" ) ) {                jQuery( this ).replaceWith( this.childNodes );            }        }).end();    },    append: function() {        return this.domManip(arguments, true, function( elem ) {            if ( this.nodeType === 1 ) {                this.appendChild( elem );            }        });    },    prepend: function() {        return this.domManip(arguments, true, function( elem ) {            if ( this.nodeType === 1 ) {                this.insertBefore( elem, this.firstChild );            }        });    },    before: function() {        if ( this[0] && this[0].parentNode ) {            return this.domManip(arguments, false, function( elem ) {                this.parentNode.insertBefore( elem, this );            });        } else if ( arguments.length ) {            var set = jQuery(arguments[0]);            set.push.apply( set, this.toArray() );            return this.pushStack( set, "before", arguments );        }    },    after: function() {        if ( this[0] && this[0].parentNode ) {            return this.domManip(arguments, false, function( elem ) {                this.parentNode.insertBefore( elem, this.nextSibling );            });        } else if ( arguments.length ) {            var set = this.pushStack( this, "after", arguments );            set.push.apply( set, jQuery(arguments[0]).toArray() );            return set;        }    },    // keepData is for internal use only--do not document    remove: function( selector, keepData ) {        for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {            if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {                if ( !keepData && elem.nodeType === 1 ) {                    jQuery.cleanData( elem.getElementsByTagName("*") );                    jQuery.cleanData( [ elem ] );                }                if ( elem.parentNode ) {                     elem.parentNode.removeChild( elem );                }            }        }        return this;    },    empty: function() {        for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {            // Remove element nodes and prevent memory leaks            if ( elem.nodeType === 1 ) {                jQuery.cleanData( elem.getElementsByTagName("*") );            }            // Remove any remaining nodes            while ( elem.firstChild ) {                elem.removeChild( elem.firstChild );            }        }        return this;    },    clone: function( events ) {        // Do the clone        var ret = this.map(function() {            if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {                // IE copies events bound via attachEvent when                // using cloneNode. Calling detachEvent on the                // clone will also remove the events from the orignal                // In order to get around this, we use innerHTML.                // Unfortunately, this means some modifications to                // attributes in IE that are actually only stored                // as properties will not be copied (such as the                // the name attribute on an input).                var html = this.outerHTML, ownerDocument = this.ownerDocument;                if ( !html ) {                    var div = ownerDocument.createElement("div");                    div.appendChild( this.cloneNode(true) );                    html = div.innerHTML;                }                return jQuery.clean([html.replace(rinlinejQuery, "")                    // Handle the case in IE 8 where action=/test/> self-closes a tag                    .replace(/=([^="'>\s]+\/)>/g, '="$1">')                    .replace(rleadingWhitespace, "")], ownerDocument)[0];            } else {                return this.cloneNode(true);            }        });        // Copy the events from the original to the clone        if ( events === true ) {            cloneCopyEvent( this, ret );            cloneCopyEvent( this.find("*"), ret.find("*") );        }        // Return the cloned set        return ret;    },    html: function( value ) {        if ( value === undefined ) {            return this[0] && this[0].nodeType === 1 ?                this[0].innerHTML.replace(rinlinejQuery, "") :                null;        // See if we can take a shortcut and just use innerHTML        } else if ( typeof value === "string" && !rnocache.test( value ) &&            (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&            !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {            value = value.replace(rxhtmlTag, fcloseTag);            try {                for ( var i = 0, l = this.length; i < l; i++ ) {                    // Remove element nodes and prevent memory leaks                    if ( this[i].nodeType === 1 ) {                        jQuery.cleanData( this[i].getElementsByTagName("*") );                        this[i].innerHTML = value;                    }                }            // If using innerHTML throws an exception, use the fallback method            } catch(e) {                this.empty().append( value );            }        } else if ( jQuery.isFunction( value ) ) {            this.each(function(i){                var self = jQuery(this), old = self.html();                self.empty().append(function(){                    return value.call( this, i, old );                });            });        } else {            this.empty().append( value );        }        return this;    },    replaceWith: function( value ) {        if ( this[0] && this[0].parentNode ) {            // Make sure that the elements are removed from the DOM before they are inserted            // this can help fix replacing a parent with child elements            if ( jQuery.isFunction( value ) ) {                return this.each(function(i) {                    var self = jQuery(this), old = self.html();                    self.replaceWith( value.call( this, i, old ) );                });            }            if ( typeof value !== "string" ) {                value = jQuery(value).detach();            }            return this.each(function() {                var next = this.nextSibling, parent = this.parentNode;                jQuery(this).remove();                if ( next ) {                    jQuery(next).before( value );                } else {                    jQuery(parent).append( value );                }            });        } else {            return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );        }    },    // 在IE6、IE7、IE8中对于select中的option元素无法通过display:none进行隐藏,这时可借助下面这个detach方法,将option先从select中detach,需要的时候再重新加到select元素中    // The new ".detach()" method allows you to remove elements from the DOM, much like the ".remove()" method. The key difference with this new method is that it doesn’t destroy the data held by jQuery on that element. This includes data added via ".data()" and any event handlers added via jQuery’s event system.    // This can be useful when you need to remove an element from the DOM, but you know you’ll need to add it back at a later stage. Its event handlers and any other data will persist.    detach: function( selector ) {        return this.remove( selector, true );    },    domManip: function( args, table, callback ) {        var results, first, value = args[0], scripts = [], fragment, parent;        // We can't cloneNode fragments that contain checked, in WebKit        if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {            return this.each(function() {                jQuery(this).domManip( args, table, callback, true );            });        }        if ( jQuery.isFunction(value) ) {            return this.each(function(i) {                var self = jQuery(this);                args[0] = value.call(this, i, table ? self.html() : undefined);                self.domManip( args, table, callback );            });        }        if ( this[0] ) {            parent = value && value.parentNode;            // If we're in a fragment, just use that instead of building a new one            if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {                results = { fragment: parent };            } else {                results = buildFragment( args, this, scripts );            }            fragment = results.fragment;            if ( fragment.childNodes.length === 1 ) {                first = fragment = fragment.firstChild;            } else {                first = fragment.firstChild;            }            if ( first ) {                table = table && jQuery.nodeName( first, "tr" );                for ( var i = 0, l = this.length; i < l; i++ ) {                    callback.call(                        table ?                            root(this[i], first) :                            this[i],                        i > 0 || results.cacheable || this.length > 1  ?                            fragment.cloneNode(true) :                            fragment                    );                }            }            if ( scripts.length ) {                jQuery.each( scripts, evalScript );            }        }        return this;        function root( elem, cur ) {            return jQuery.nodeName(elem, "table") ?                (elem.getElementsByTagName("tbody")[0] ||                elem.appendChild(elem.ownerDocument.createElement("tbody"))) :                elem;        }    }});function cloneCopyEvent(orig, ret) {    var i = 0;    ret.each(function() {        if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {            return;        }        var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;        if ( events ) {            delete curData.handle;            curData.events = {};            for ( var type in events ) {                for ( var handler in events[ type ] ) {                    jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );                }            }        }    });}function buildFragment( args, nodes, scripts ) {    var fragment, cacheable, cacheresults,        doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);    // Only cache "small" (1/2 KB) strings that are associated with the main document    // Cloning options loses the selected state, so don't cache them    // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment    // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache    if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&        !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {        cacheable = true;        cacheresults = jQuery.fragments[ args[0] ];        if ( cacheresults ) {            if ( cacheresults !== 1 ) {                fragment = cacheresults;            }        }    }    if ( !fragment ) {        fragment = doc.createDocumentFragment();        jQuery.clean( args, doc, fragment, scripts );    }    if ( cacheable ) {        jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;    }    return { fragment: fragment, cacheable: cacheable };}jQuery.fragments = {};jQuery.each({    appendTo: "append",    prependTo: "prepend",    insertBefore: "before",    insertAfter: "after",    replaceAll: "replaceWith"}, function( name, original ) {    jQuery.fn[ name ] = function( selector ) {        var ret = [], insert = jQuery( selector ),            parent = this.length === 1 && this[0].parentNode;        if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {            insert[ original ]( this[0] );            return this;        } else {            for ( var i = 0, l = insert.length; i < l; i++ ) {                var elems = (i > 0 ? this.clone(true) : this).get();                jQuery.fn[ original ].apply( jQuery(insert[i]), elems );                ret = ret.concat( elems );            }            return this.pushStack( ret, name, insert.selector );        }    };});jQuery.extend({    clean: function( elems, context, fragment, scripts ) {        context = context || document;        // !context.createElement fails in IE with an error but returns typeof 'object'        if ( typeof context.createElement === "undefined" ) {            context = context.ownerDocument || context[0] && context[0].ownerDocument || document;        }        var ret = [];        for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {            if ( typeof elem === "number" ) {                elem += "";            }            if ( !elem ) {                continue;            }            // Convert html string into DOM nodes            if ( typeof elem === "string" && !rhtml.test( elem ) ) {                elem = context.createTextNode( elem );            } else if ( typeof elem === "string" ) {                // Fix "XHTML"-style tags in all browsers                elem = elem.replace(rxhtmlTag, fcloseTag);                // Trim whitespace, otherwise indexOf won't work as expected                var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),                    wrap = wrapMap[ tag ] || wrapMap._default,                    depth = wrap[0],                    div = context.createElement("div");                // Go to html and back, then peel off extra wrappers                div.innerHTML = wrap[1] + elem + wrap[2];                // Move to the right depth                while ( depth-- ) {                    div = div.lastChild;                }                // Remove IE's autoinserted <tbody> from table fragments                if ( !jQuery.support.tbody ) {                    // String was a <table>, *may* have spurious <tbody>                    var hasBody = rtbody.test(elem),                        tbody = tag === "table" && !hasBody ?                            div.firstChild && div.firstChild.childNodes :                            // String was a bare <thead> or <tfoot>                            wrap[1] === "<table>" && !hasBody ?                                div.childNodes :                                [];                    for ( var j = tbody.length - 1; j >= 0 ; --j ) {                        if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {                            tbody[ j ].parentNode.removeChild( tbody[ j ] );                        }                    }                }                // IE completely kills leading whitespace when innerHTML is used                if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {                    div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );                }                elem = div.childNodes;            }            if ( elem.nodeType ) {                ret.push( elem );            } else {                ret = jQuery.merge( ret, elem );            }        }        if ( fragment ) {            for ( var i = 0; ret[i]; i++ ) {                if ( scripts && 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"))) );                    }                    fragment.appendChild( ret[i] );                }            }        }        return ret;    },    cleanData: function( elems ) {        var data, id, cache = jQuery.cache,            special = jQuery.event.special,            deleteExpando = jQuery.support.deleteExpando;        for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {            id = elem[ jQuery.expando ];            if ( id ) {                data = cache[ id ];                if ( data.events ) {                    for ( var type in data.events ) {                        if ( special[ type ] ) {                            jQuery.event.remove( elem, type );                        } else {                            removeEvent( elem, type, data.handle );                        }                    }                }                if ( deleteExpando ) {                    delete elem[ jQuery.expando ];                } else if ( elem.removeAttribute ) {                    elem.removeAttribute( jQuery.expando );                }                delete cache[ id ];            }        }    }});
function evalScript( i, elem ) {    if ( elem.src ) {        jQuery.ajax({            url: elem.src,            async: false,            dataType: "script"        });    } else {        jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );    }    if ( elem.parentNode ) {        elem.parentNode.removeChild( elem );    }}