Fix #13434: native-API selector module

What's out:
* 6 KB
* attribute not equal selector
* positional selectors (:first; :eq(n); :odd; etc.)
* type selectors (:input; :checkbox; :button; etc.)
* state-based selectors (:animated; :visible; :hidden; etc.)
* :has(selector)
* custom selectors
* leading combinators (e.g., $collection.find("> *"))
* reliable functionality on XML fragments
* requiring all parts of a selector to match elements under context (e.g., $div.find("div > *") now matches children of $div)
* matching against non-elements
* reliable sorting of disconnected nodes
This commit is contained in:
Richard Gibson 2013-02-18 23:52:29 -05:00 committed by Dave Methvin
parent 0618710913
commit 1083f82d1e
2 changed files with 151 additions and 18 deletions

View File

@ -22,7 +22,7 @@ module.exports = function( grunt ) {
files: distpaths files: distpaths
}, },
selector: { selector: {
destFile: "src/selector.js", destFile: "src/selector-sizzle.js",
apiFile: "src/sizzle-jquery.js", apiFile: "src/sizzle-jquery.js",
srcFile: "src/sizzle/sizzle.js" srcFile: "src/sizzle/sizzle.js"
}, },
@ -39,10 +39,9 @@ module.exports = function( grunt ) {
"src/queue.js", "src/queue.js",
"src/attributes.js", "src/attributes.js",
"src/event.js", "src/event.js",
"src/selector.js", { flag: "sizzle", src: "src/selector-sizzle.js", alt: "src/selector-native.js" },
"src/traversing.js", "src/traversing.js",
"src/manipulation.js", "src/manipulation.js",
{ flag: "css", src: "src/css.js" }, { flag: "css", src: "src/css.js" },
"src/serialize.js", "src/serialize.js",
{ flag: "event-alias", src: "src/event-alias.js" }, { flag: "event-alias", src: "src/event-alias.js" },
@ -145,8 +144,7 @@ module.exports = function( grunt ) {
}); });
}); });
// Build src/selector.js grunt.registerTask( "selector", "Build Sizzle-based selector module", function() {
grunt.registerTask( "selector", "Build src/selector.js", function() {
var cfg = grunt.config("selector"), var cfg = grunt.config("selector"),
name = cfg.destFile, name = cfg.destFile,
@ -190,7 +188,7 @@ module.exports = function( grunt ) {
// Rejoin the pieces // Rejoin the pieces
compiled = parts.join(""); compiled = parts.join("");
grunt.verbose.write("Injected sizzle-jquery.js into sizzle.js"); grunt.verbose.writeln("Injected " + cfg.apiFile + " into " + cfg.srcFile);
// Write concatenated source to file, and ensure newline-only termination // Write concatenated source to file, and ensure newline-only termination
grunt.file.write( name, compiled.replace( /\x0d\x0a/g, "\x0a" ) ); grunt.file.write( name, compiled.replace( /\x0d\x0a/g, "\x0a" ) );
@ -328,37 +326,47 @@ module.exports = function( grunt ) {
var flag = filepath.flag, var flag = filepath.flag,
specified = false, specified = false,
omit = false, omit = false,
message = ""; messages = [];
if ( flag ) { if ( flag ) {
if ( excluded[ flag ] !== undefined ) { if ( excluded[ flag ] !== undefined ) {
message = ( "Excluding " + flag ).red; messages.push([
( "Excluding " + flag ).red,
( "(" + filepath.src + ")" ).grey
]);
specified = true; specified = true;
omit = true; omit = !filepath.alt;
} else { if ( !omit ) {
message = ( "Including " + flag ).green; flag += " alternate";
filepath.src = filepath.alt;
}
}
if ( excluded[ flag ] === undefined ) {
messages.push([
( "Including " + flag ).green,
( "(" + filepath.src + ")" ).grey
]);
// If this module was actually specified by the // If this module was actually specified by the
// builder, then st the flag to include it in the // builder, then set the flag to include it in the
// output list // output list
if ( modules[ "+" + flag ] ) { if ( modules[ "+" + flag ] ) {
specified = true; specified = true;
} }
} }
filepath = filepath.src;
// Only display the inclusion/exclusion list when handling // Only display the inclusion/exclusion list when handling
// an explicit list. // an explicit list.
// //
// Additionally, only display modules that have been specified // Additionally, only display modules that have been specified
// by the user // by the user
if ( explicit && specified ) { if ( explicit && specified ) {
grunt.log.writetableln([ 27, 30 ], [ messages.forEach(function( message ) {
message, grunt.log.writetableln( [ 27, 30 ], message );
( "(" + filepath.src + ")").grey });
]);
} }
filepath = filepath.src;
} }
if ( !omit ) { if ( !omit ) {

125
src/selector-native.js Normal file
View File

@ -0,0 +1,125 @@
var selector_hasDuplicate,
matches = docElem.matchesSelector ||
docElem.mozMatchesSelector ||
docElem.webkitMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector,
selector_sortOrder = function( a, b ) {
// Flag for duplicate removal
if ( a === b ) {
selector_hasDuplicate = true;
return 0;
}
var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
if ( compare ) {
// Disconnected nodes
if ( compare & 1 ) {
// Choose the first element that is related to our document
if ( a === document || jQuery.contains(document, a) ) {
return -1;
}
if ( b === document || jQuery.contains(document, b) ) {
return 1;
}
// Maintain original order
return 0;
}
return compare & 4 ? -1 : 1;
}
// Not directly comparable, sort on existence of method
return a.compareDocumentPosition ? -1 : 1;
};
jQuery.extend({
find: function( selector, context, results, seed ) {
var elem,
i = 0;
results = results || [];
context = context || document;
if ( seed ) {
while ( (elem = seed[i++]) ) {
if ( jQuery.find.matchesSelector(elem, selector) ) {
results.push( elem );
}
}
} else {
jQuery.merge( results, context.querySelectorAll(selector) );
}
return results;
},
unique: function( results ) {
var elem,
duplicates = [],
i = 0,
j = 0;
selector_hasDuplicate = false;
results.sort( selector_sortOrder );
if ( selector_hasDuplicate ) {
while ( (elem = results[i++]) ) {
if ( elem === results[ i ] ) {
j = duplicates.push( i );
}
}
while ( j-- ) {
results.splice( duplicates[ j ], 1 );
}
}
return results;
},
text: function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if ( !nodeType ) {
// If no nodeType, this is expected to be an array
while ( (node = elem[i++]) ) {
// Do not traverse comment nodes
ret += jQuery.text( node );
}
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
return elem.textContent;
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
},
contains: function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains(bup) );
},
isXMLDoc: function( elem ) {
return (elem.ownerDocument || elem).documentElement.nodeName !== "HTML";
},
expr: {
match: {
needsContext: /^[\x20\t\r\n\f]*[>+~]/
}
}
});
jQuery.extend( jQuery.find, {
matches: function( expr, elements ) {
return jQuery.find( expr, null, null, elements );
},
matchesSelector: function( elem, expr ) {
return matches.call( elem, expr );
}
});