| 2473 | } |
| 2474 | |
| 2475 | function addCombinator( matcher, combinator, base ) { |
| 2476 | var dir = combinator.dir, |
| 2477 | checkNonElements = base && dir === "parentNode", |
| 2478 | doneName = done++; |
| 2479 | |
| 2480 | return combinator.first ? |
| 2481 | // Check against closest ancestor/preceding element |
| 2482 | function( elem, context, xml ) { |
| 2483 | while ( (elem = elem[ dir ]) ) { |
| 2484 | if ( elem.nodeType === 1 || checkNonElements ) { |
| 2485 | return matcher( elem, context, xml ); |
| 2486 | } |
| 2487 | } |
| 2488 | } : |
| 2489 | |
| 2490 | // Check against all ancestor/preceding elements |
| 2491 | function( elem, context, xml ) { |
| 2492 | var data, cache, outerCache, |
| 2493 | dirkey = dirruns + " " + doneName; |
| 2494 | |
| 2495 | // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching |
| 2496 | if ( xml ) { |
| 2497 | while ( (elem = elem[ dir ]) ) { |
| 2498 | if ( elem.nodeType === 1 || checkNonElements ) { |
| 2499 | if ( matcher( elem, context, xml ) ) { |
| 2500 | return true; |
| 2501 | } |
| 2502 | } |
| 2503 | } |
| 2504 | } else { |
| 2505 | while ( (elem = elem[ dir ]) ) { |
| 2506 | if ( elem.nodeType === 1 || checkNonElements ) { |
| 2507 | outerCache = elem[ expando ] || (elem[ expando ] = {}); |
| 2508 | if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { |
| 2509 | if ( (data = cache[1]) === true || data === cachedruns ) { |
| 2510 | return data === true; |
| 2511 | } |
| 2512 | } else { |
| 2513 | cache = outerCache[ dir ] = [ dirkey ]; |
| 2514 | cache[1] = matcher( elem, context, xml ) || cachedruns; |
| 2515 | if ( cache[1] === true ) { |
| 2516 | return true; |
| 2517 | } |
| 2518 | } |
| 2519 | } |
| 2520 | } |
| 2521 | } |
| 2522 | }; |
| 2523 | } |
| 2524 | |
| 2525 | function elementMatcher( matchers ) { |
| 2526 | return matchers.length > 1 ? |