Manipulation: privatize buildFragment() function

Fixes gh-2224
This commit is contained in:
Timmy Willison 2015-05-05 11:44:55 -07:00
parent b9b5c23fd7
commit 63c1414a54
11 changed files with 265 additions and 202 deletions

View File

@ -1,13 +1,11 @@
define([
"../core",
"./var/rsingleTag",
"../manipulation/buildFragment",
// This is the only module that needs core/support
"./support",
// buildFragment
"../manipulation"
], function( jQuery, rsingleTag, support ) {
"./support"
], function( jQuery, rsingleTag, buildFragment, support ) {
// data: string of html
// context (optional): If specified, the fragment will be created in this context,
@ -35,7 +33,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,17 @@ define([
"./var/push",
"./var/deletedIds",
"./core/access",
"./manipulation/var/rcheckableType",
"./manipulation/var/rtagName",
"./manipulation/var/rscriptType",
"./manipulation/var/rleadingWhitespace",
"./manipulation/var/nodeNames",
"./manipulation/createSafeFragment",
"./manipulation/wrapMap",
"./manipulation/getAll",
"./manipulation/setGlobalEval",
"./manipulation/buildFragment",
"./manipulation/support",
"./core/init",
@ -12,96 +22,22 @@ define([
"./traversing",
"./selector",
"./event"
], function( jQuery, concat, push, deletedIds, access, rcheckableType, support ) {
], function( jQuery, concat, push, deletedIds, access,
rcheckableType, rtagName, rscriptType, rleadingWhitespace, nodeNames,
createSafeFragment, wrapMap, getAll, setGlobalEval,
buildFragment, support ) {
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();
if ( safeFrag.createElement ) {
while ( list.length ) {
safeFrag.createElement(
list.pop()
);
}
}
return safeFrag;
}
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" +
"details|dialog|figcaption|figure|footer|header|hgroup|main|" +
"mark|meter|nav|output|picture|progress|section|summary|template|time|video",
rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
rleadingWhitespace = /^\s+/,
var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ),
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 = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
// Support: IE8
param: [ 1, "<object>", "</object>" ],
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>" ],
// IE8 can't serialize link, script, style, or any html5 (NoScope) tags,
// unless wrapped in a div with non-breaking characters in front of it.
_default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
},
safeFragment = createSafeFragment( document ),
fragmentDiv = safeFragment.appendChild( document.createElement("div") );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
function getAll( context, tag ) {
var elems, elem,
i = 0,
found = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== "undefined" ?
context.querySelectorAll( tag || "*" ) :
undefined;
if ( !found ) {
for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
if ( !tag || jQuery.nodeName( elem, tag ) ) {
found.push( elem );
} else {
jQuery.merge( found, getAll( elem, tag ) );
}
}
}
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], found ) :
found;
}
// Manipulating tables requires a tbody
function manipulationTarget( elem, content ) {
if ( jQuery.nodeName( elem, "table" ) &&
@ -128,21 +64,7 @@ function restoreScript( elem ) {
return elem;
}
// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
var elem,
i = 0;
for ( ; (elem = elems[i]) != null; i++ ) {
jQuery._data(
elem,
"globalEval",
!refElements || jQuery._data( refElements[i], "globalEval" )
);
}
}
function cloneCopyEvent( src, dest ) {
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
return;
}
@ -278,107 +200,6 @@ jQuery.extend({
return clone;
},
buildFragment: function( elems, context, scripts, selection, ignored ) {
var j, elem, contains,
tmp, tag, wrap,
l = elems.length,
// Ensure a safe fragment
safe = createSafeFragment( context ),
nodes = [],
i = 0;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
// Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
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 || safe.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;
}
// Manually add leading whitespace removed by IE
if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
}
jQuery.merge( nodes, tmp.childNodes );
// Fix #12392 for WebKit and IE > 9
tmp.textContent = "";
// Fix #12392 for oldIE
while ( tmp.firstChild ) {
tmp.removeChild( tmp.firstChild );
}
// Remember the top-level container for proper cleanup
tmp = safe.lastChild;
}
}
}
// Fix #11356: Clear elements from fragment
if ( tmp ) {
safe.removeChild( tmp );
}
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( safe.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 );
}
}
}
}
tmp = null;
return safe;
},
cleanData: function( elems, /* internal */ acceptData ) {
var elem, type, id, data,
i = 0,
@ -626,7 +447,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,118 @@
define([
"../core",
"./var/rtagName",
"./var/rscriptType",
"./var/rleadingWhitespace",
"./createSafeFragment",
"./wrapMap",
"./getAll",
"./setGlobalEval",
"./support"
], function( jQuery, rtagName, rscriptType, rleadingWhitespace,
createSafeFragment, wrapMap, getAll, setGlobalEval, support ) {
var rhtml = /<|&#?\w+;/;
function buildFragment( elems, context, scripts, selection, ignored ) {
var j, elem, contains,
tmp, tag, wrap,
l = elems.length,
// Ensure a safe fragment
safe = createSafeFragment( context ),
nodes = [],
i = 0;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
// Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
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 || safe.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;
}
// Manually add leading whitespace removed by IE
if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
}
jQuery.merge( nodes, tmp.childNodes );
// Fix #12392 for WebKit and IE > 9
tmp.textContent = "";
// Fix #12392 for oldIE
while ( tmp.firstChild ) {
tmp.removeChild( tmp.firstChild );
}
// Remember the top-level container for proper cleanup
tmp = safe.lastChild;
}
}
}
// Fix #11356: Clear elements from fragment
if ( tmp ) {
safe.removeChild( tmp );
}
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( safe.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 );
}
}
}
}
tmp = null;
return safe;
}
return buildFragment;
});

View File

@ -0,0 +1,20 @@
define([
"./var/nodeNames"
], function( nodeNames ) {
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();
if ( safeFrag.createElement ) {
while ( list.length ) {
safeFrag.createElement(
list.pop()
);
}
}
return safeFrag;
}
return createSafeFragment;
});

View File

@ -0,0 +1,33 @@
define([
"../core"
], function( jQuery ) {
function getAll( context, tag ) {
var elems, elem,
i = 0,
found = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== "undefined" ?
context.querySelectorAll( tag || "*" ) :
undefined;
if ( !found ) {
for ( found = [], elems = context.childNodes || context;
( elem = elems[i] ) != null;
i++
) {
if ( !tag || jQuery.nodeName( elem, tag ) ) {
found.push( elem );
} else {
jQuery.merge( found, getAll( elem, tag ) );
}
}
}
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], found ) :
found;
}
return getAll;
});

View File

@ -0,0 +1,19 @@
define([
"../core"
], function( jQuery ) {
// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
var elem,
i = 0;
for ( ; ( elem = elems[i] ) != null; i++ ) {
jQuery._data(
elem,
"globalEval",
!refElements || jQuery._data( refElements[i], "globalEval" )
);
}
}
return setGlobalEval;
});

View File

@ -0,0 +1,5 @@
define(function() {
return "abbr|article|aside|audio|bdi|canvas|data|datalist|" +
"details|dialog|figcaption|figure|footer|header|hgroup|main|" +
"mark|meter|nav|output|picture|progress|section|summary|template|time|video";
});

View File

@ -0,0 +1,3 @@
define(function() {
return ( /^\s+/ );
});

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,40 @@
define([
"./support"
], function( support ) {
// We have to close these tags to support XHTML (#13200)
var wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
// Support: IE8
param: [ 1, "<object>", "</object>" ],
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>" ],
// IE8 can't serialize link, script, style, or any html5 (NoScope) tags,
// unless wrapped in a div with non-breaking characters in front of it.
_default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
};
// Support: IE8-IE9
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
return wrapMap;
});