Manipulation: privatize buildFragment() function

Fixes gh-2224
This commit is contained in:
Timmy Willison 2015-05-05 11:44:55 -07:00
parent 6d7ef56ed3
commit a74320fca8
8 changed files with 199 additions and 157 deletions

View File

@ -2,13 +2,11 @@ define([
"../core",
"../var/document",
"./var/rsingleTag",
"../manipulation/buildFragment",
// This is the only module that needs core/support
"./support",
// buildFragment
"../manipulation"
], function( jQuery, document, rsingleTag, support ) {
"./support"
], function( jQuery, document, rsingleTag, buildFragment, support ) {
// data: string of html
// context (optional): If specified, the fragment will be created in this context,
@ -36,7 +34,7 @@ jQuery.parseHTML = function( data, context, keepScripts ) {
return [ context.createElement( parsed[1] ) ];
}
parsed = jQuery.buildFragment( [ data ], context, scripts );
parsed = buildFragment( [ data ], context, scripts );
if ( scripts && scripts.length ) {
jQuery( scripts ).remove();

View File

@ -4,7 +4,14 @@ define([
"./var/push",
"./core/access",
"./manipulation/var/rcheckableType",
"./manipulation/var/rtagName",
"./manipulation/var/rscriptType",
"./manipulation/wrapMap",
"./manipulation/getAll",
"./manipulation/setGlobalEval",
"./manipulation/buildFragment",
"./manipulation/support",
"./data/var/dataPriv",
"./data/var/dataUser",
@ -13,49 +20,18 @@ define([
"./traversing",
"./selector",
"./event"
], function( jQuery, concat, push, access, rcheckableType, support, dataPriv, dataUser ) {
], function( jQuery, concat, push, access,
rcheckableType, rtagName, rscriptType,
wrapMap, getAll, setGlobalEval, buildFragment, support,
dataPriv, dataUser ) {
var
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
rtagName = /<([\w:-]+)/,
rhtml = /<|&#?\w+;/,
rnoInnerhtml = /<(?:script|style|link)/i,
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /^$|\/(?:java|ecma)script/i,
rscriptTypeMasked = /^true\/(.*)/,
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
// We have to close these tags to support XHTML (#13200)
wrapMap = {
// Support: IE9
option: [ 1, "<select multiple='multiple'>", "</select>" ],
thead: [ 1, "<table>", "</table>" ],
// Some of the following wrappers are not fully defined, because
// their parent elements (except for "table" element) could be omitted
// since browser parsers are smart enough to auto-insert them
// Support: Android 2.3
// Android browser doesn't auto-insert colgroup
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
// Auto-insert "tbody" element
tr: [ 2, "<table>", "</table>" ],
// Auto-insert "tbody" and "tr" elements
td: [ 3, "<table>", "</table>" ],
_default: [ 0, "", "" ]
};
// Support: IE9
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
function manipulationTarget( elem, content ) {
if ( jQuery.nodeName( elem, "table" ) &&
@ -84,18 +60,6 @@ function restoreScript( elem ) {
return elem;
}
// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
var i = 0,
l = elems.length;
for ( ; i < l; i++ ) {
dataPriv.set(
elems[ i ], "globalEval", !refElements || dataPriv.get( refElements[ i ], "globalEval" )
);
}
}
function cloneCopyEvent( src, dest ) {
var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
@ -130,20 +94,6 @@ function cloneCopyEvent( src, dest ) {
}
}
function getAll( context, tag ) {
// Support: IE9-11+
// Use typeof to avoid zero-argument method invocation on host objects (#15151)
var ret = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== "undefined" ?
context.querySelectorAll( tag || "*" ) :
[];
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], ret ) :
ret;
}
// Fix IE bugs, see support tests
function fixInput( src, dest ) {
var nodeName = dest.nodeName.toLowerCase();
@ -205,94 +155,6 @@ jQuery.extend({
return clone;
},
buildFragment: function( elems, context, scripts, selection, ignored ) {
var elem, tmp, tag, wrap, contains, j,
fragment = context.createDocumentFragment(),
nodes = [],
i = 0,
l = elems.length;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
// Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
// Support: Android<4.1, PhantomJS<2
// push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
// Convert non-html into a text node
} else if ( !rhtml.test( elem ) ) {
nodes.push( context.createTextNode( elem ) );
// Convert html into DOM nodes
} else {
tmp = tmp || fragment.appendChild( context.createElement("div") );
// Deserialize a standard representation
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
// Descend through wrappers to the right content
j = wrap[ 0 ];
while ( j-- ) {
tmp = tmp.lastChild;
}
// Support: Android<4.1, PhantomJS<2
// push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( nodes, tmp.childNodes );
// Remember the top-level container
tmp = fragment.firstChild;
// Ensure the created nodes are orphaned (#12392)
tmp.textContent = "";
}
}
}
// Remove wrapper from fragment
fragment.textContent = "";
i = 0;
while ( (elem = nodes[ i++ ]) ) {
// Skip elements already in the context collection (trac-4087)
if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
if ( ignored ) {
ignored.push( elem );
}
continue;
}
contains = jQuery.contains( elem.ownerDocument, elem );
// Append to fragment
tmp = getAll( fragment.appendChild( elem ), "script" );
// Preserve script evaluation history
if ( contains ) {
setGlobalEval( tmp );
}
// Capture executables
if ( scripts ) {
j = 0;
while ( (elem = tmp[ j++ ]) ) {
if ( rscriptType.test( elem.type || "" ) ) {
scripts.push( elem );
}
}
}
}
return fragment;
},
cleanData: function( elems ) {
var data, elem, type,
special = jQuery.event.special,
@ -500,7 +362,7 @@ jQuery.fn.extend({
}
if ( l ) {
fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this, ignored );
fragment = buildFragment( args, this[ 0 ].ownerDocument, false, this, ignored );
first = fragment.firstChild;
if ( fragment.childNodes.length === 1 ) {

View File

@ -0,0 +1,101 @@
define([
"../core",
"./var/rtagName",
"./var/rscriptType",
"./wrapMap",
"./getAll",
"./setGlobalEval"
], function( jQuery, rtagName, rscriptType, wrapMap, getAll, setGlobalEval ) {
var rhtml = /<|&#?\w+;/;
function buildFragment( elems, context, scripts, selection, ignored ) {
var elem, tmp, tag, wrap, contains, j,
fragment = context.createDocumentFragment(),
nodes = [],
i = 0,
l = elems.length;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
// Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
// Support: Android<4.1, PhantomJS<2
// push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
// Convert non-html into a text node
} else if ( !rhtml.test( elem ) ) {
nodes.push( context.createTextNode( elem ) );
// Convert html into DOM nodes
} else {
tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
// Deserialize a standard representation
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
// Descend through wrappers to the right content
j = wrap[ 0 ];
while ( j-- ) {
tmp = tmp.lastChild;
}
// Support: Android<4.1, PhantomJS<2
// push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( nodes, tmp.childNodes );
// Remember the top-level container
tmp = fragment.firstChild;
// Ensure the created nodes are orphaned (#12392)
tmp.textContent = "";
}
}
}
// Remove wrapper from fragment
fragment.textContent = "";
i = 0;
while ( ( elem = nodes[ i++ ] ) ) {
// Skip elements already in the context collection (trac-4087)
if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
if ( ignored ) {
ignored.push( elem );
}
continue;
}
contains = jQuery.contains( elem.ownerDocument, elem );
// Append to fragment
tmp = getAll( fragment.appendChild( elem ), "script" );
// Preserve script evaluation history
if ( contains ) {
setGlobalEval( tmp );
}
// Capture executables
if ( scripts ) {
j = 0;
while ( ( elem = tmp[ j++ ] ) ) {
if ( rscriptType.test( elem.type || "" ) ) {
scripts.push( elem );
}
}
}
}
return fragment;
}
return buildFragment;
});

View File

@ -0,0 +1,20 @@
define([
"../core"
], function( jQuery ) {
function getAll( context, tag ) {
// Support: IE9-11+
// Use typeof to avoid zero-argument method invocation on host objects (#15151)
var ret = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== "undefined" ?
context.querySelectorAll( tag || "*" ) :
[];
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], ret ) :
ret;
}
return getAll;
});

View File

@ -0,0 +1,20 @@
define([
"../data/var/dataPriv"
], function( dataPriv ) {
// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
var i = 0,
l = elems.length;
for ( ; i < l; i++ ) {
dataPriv.set(
elems[ i ],
"globalEval",
!refElements || dataPriv.get( refElements[ i ], "globalEval" )
);
}
}
return setGlobalEval;
});

View File

@ -0,0 +1,3 @@
define(function() {
return ( /^$|\/(?:java|ecma)script/i );
});

View File

@ -0,0 +1,3 @@
define(function() {
return ( /<([\w:-]+)/ );
});

View File

@ -0,0 +1,35 @@
define(function() {
// We have to close these tags to support XHTML (#13200)
var wrapMap = {
// Support: IE9
option: [ 1, "<select multiple='multiple'>", "</select>" ],
thead: [ 1, "<table>", "</table>" ],
// Some of the following wrappers are not fully defined, because
// their parent elements (except for "table" element) could be omitted
// since browser parsers are smart enough to auto-insert them
// Support: Android 2.3
// Android browser doesn't auto-insert colgroup
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
// Auto-insert "tbody" element
tr: [ 2, "<table>", "</table>" ],
// Auto-insert "tbody" and "tr" elements
td: [ 3, "<table>", "</table>" ],
_default: [ 0, "", "" ]
};
// Support: IE9
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
return wrapMap;
});