aboutsummaryrefslogtreecommitdiffstats
path: root/src/selector-native.js
blob: b245e33506e7e025e0d11e73303adae669a605d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * Optional limited selector module for custom builds.
 *
 * Note that this DOES NOT SUPPORT many documented jQuery
 * features in exchange for its smaller size:
 *
 * * 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) in browsers without native support
 * * :not(complex selector) in IE
 * * custom selectors via jQuery extensions
 * * Reliable functionality on XML fragments
 * * Matching against non-elements
 * * Reliable sorting of disconnected nodes
 * * querySelectorAll bug fixes (e.g., unreliable :focus on WebKit)
 *
 * If any of these are unacceptable tradeoffs, either use the full
 * selector engine or  customize this stub for the project's specific
 * needs.
 */

import { jQuery } from "./core.js";
import { document } from "./var/document.js";
import { whitespace } from "./var/whitespace.js";
import { isIE } from "./var/isIE.js";
import { rleadingCombinator } from "./selector/var/rleadingCombinator.js";
import { rdescend } from "./selector/var/rdescend.js";
import { rsibling } from "./selector/var/rsibling.js";
import { matches } from "./selector/var/matches.js";
import { testContext } from "./selector/testContext.js";
import { filterMatchExpr } from "./selector/filterMatchExpr.js";
import { preFilter } from "./selector/preFilter.js";
import { tokenize } from "./selector/tokenize.js";
import { toSelector } from "./selector/toSelector.js";

// The following utils are attached directly to the jQuery object.
import "./selector/escapeSelector.js";
import "./selector/uniqueSort.js";

var matchExpr = jQuery.extend( {
	needsContext: new RegExp( "^" + whitespace + "*[>+~]" )
}, filterMatchExpr );

jQuery.extend( {
	find: function( selector, context, results, seed ) {
		var elem, nid, groups, newSelector,
			newContext = context && context.ownerDocument,

			// nodeType defaults to 9, since context defaults to document
			nodeType = context ? context.nodeType : 9,
			i = 0;

		results = results || [];
		context = context || document;

		// Same basic safeguard as in the full selector module
		if ( !selector || typeof selector !== "string" ) {
			return results;
		}

		// Early return if context is not an element, document or document fragment
		if ( nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
			return [];
		}

		if ( seed ) {
			while ( ( elem = seed[ i++ ] ) ) {
				if ( jQuery.find.matchesSelector( elem, selector ) ) {
					results.push( elem );
				}
			}
		} else {

			newSelector = selector;
			newContext = context;

			// qSA considers elements outside a scoping root when evaluating child or
			// descendant combinators, which is not what we want.
			// In such cases, we work around the behavior by prefixing every selector in the
			// list with an ID selector referencing the scope context.
			// The technique has to be used as well when a leading combinator is used
			// as such selectors are not recognized by querySelectorAll.
			// Thanks to Andrew Dupont for this technique.
			if ( nodeType === 1 &&
				( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {

				// Expand context for sibling selectors
				newContext = rsibling.test( selector ) &&
					testContext( context.parentNode ) ||
					context;

				// Outside of IE, if we're not changing the context we can
				// use :scope instead of an ID.
				if ( newContext !== context || isIE ) {

					// Capture the context ID, setting it first if necessary
					if ( ( nid = context.getAttribute( "id" ) ) ) {
						nid = jQuery.escapeSelector( nid );
					} else {
						context.setAttribute( "id", ( nid = jQuery.expando ) );
					}
				}

				// Prefix every selector in the list
				groups = tokenize( selector );
				i = groups.length;
				while ( i-- ) {
					groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
						toSelector( groups[ i ] );
				}
				newSelector = groups.join( "," );
			}

			try {
				jQuery.merge( results, newContext.querySelectorAll( newSelector ) );
			} finally {
				if ( nid === jQuery.expando ) {
					context.removeAttribute( "id" );
				}
			}
		}

		return results;
	},
	expr: {

		// Can be adjusted by the user
		cacheLength: 50,

		match: matchExpr,
		preFilter: preFilter
	}
} );

jQuery.extend( jQuery.find, {
	matches: function( expr, elements ) {
		return jQuery.find( expr, null, null, elements );
	},
	matchesSelector: function( elem, expr ) {
		return matches.call( elem, expr );
	},
	tokenize: tokenize
} );

export { jQuery, jQuery as $ };