|
540 | 540 | return path; |
541 | 541 | } |
542 | 542 |
|
543 | | - function queryElements({ element, prefix, type, selector }) { |
544 | | - let elements = new Map(); |
545 | | - if (!element) element = document; |
| 543 | + function queryElements({ element = document, prefix, type, selector }) { |
| 544 | + let elements = new Set(); |
| 545 | + let queryTypes = [ |
| 546 | + "selector", |
| 547 | + "closest", |
| 548 | + "parent", |
| 549 | + "next", |
| 550 | + "previous", |
| 551 | + "document", |
| 552 | + "frame", |
| 553 | + "top" |
| 554 | + ]; |
| 555 | + |
546 | 556 | let hasAttribute = false; |
547 | 557 |
|
548 | | - if (selector) { |
549 | | - if (!type) type = ["selector"]; |
550 | | - else if (!Array.isArray(type)) type = [type]; |
551 | | - } else type = ["selector", "closest", "parent", "next", "previous", "document"]; |
| 558 | + if (!prefix) { |
| 559 | + for (let attr of element.attributes) { |
| 560 | + let parts = attr.name.split("-"); |
| 561 | + if (parts.length < 2) continue; |
| 562 | + |
| 563 | + let possibleType = parts.pop(); |
| 564 | + if (queryTypes.includes(possibleType)) { |
| 565 | + type = [possibleType]; |
| 566 | + prefix = parts.join("-"); |
| 567 | + break; |
| 568 | + } |
| 569 | + } |
| 570 | + } else if (!type) { |
| 571 | + for (let i = 0; i < queryTypes.length; i++) { |
| 572 | + if (element.hasAttribute(`${prefix}-${queryTypes[i]}`)) { |
| 573 | + type = [queryTypes[i]]; |
| 574 | + } |
| 575 | + } |
| 576 | + } |
| 577 | + |
| 578 | + if (!prefix) return false; |
| 579 | + |
| 580 | + if (!type) type = selector ? ["selector"] : queryTypes; |
| 581 | + |
| 582 | + if (!Array.isArray(type)) type = [type]; |
552 | 583 |
|
553 | 584 | for (let i = 0; i < type.length; i++) { |
554 | | - let Selector = selector; |
555 | | - if (!Selector && element.nodeType !== 9) { |
| 585 | + if (!selector && element.nodeType !== 9) { |
556 | 586 | let name = prefix + "-" + type[i]; |
557 | 587 | if (!element.hasAttribute(name)) continue; |
558 | 588 | hasAttribute = true; |
559 | | - Selector = element.getAttribute(name); |
| 589 | + selector = element.getAttribute(name); |
| 590 | + type = [type[i]]; |
560 | 591 | } |
561 | 592 |
|
562 | | - if (Selector) { |
563 | | - let selectors = Selector.split(/,(?![^()]*\))/g); |
| 593 | + let mainElement = element; |
| 594 | + if ( |
| 595 | + [ |
| 596 | + "parent", |
| 597 | + "next", |
| 598 | + "previous", |
| 599 | + "document", |
| 600 | + "frame", |
| 601 | + "top" |
| 602 | + ].includes(type[i]) |
| 603 | + ) { |
| 604 | + mainElement = queryType(mainElement, type[i]); |
| 605 | + } |
| 606 | + |
| 607 | + if (!selector) { |
| 608 | + elements.add(mainElement); |
| 609 | + } else { |
| 610 | + let selectors = selector.split(/,(?![^()]*\))/g); |
564 | 611 |
|
565 | 612 | for (let j = 0; j < selectors.length; j++) { |
| 613 | + if (!selectors[j]) continue; |
| 614 | + |
| 615 | + let queriedElement = mainElement; |
| 616 | + |
566 | 617 | if (selectors[j].includes("@")) { |
567 | 618 | selectors[j] = checkMediaQueries(selectors[j]); |
568 | 619 | if (selectors[j] === false) continue; |
569 | 620 | } |
570 | 621 |
|
571 | | - let queriedElement = element; |
| 622 | + if (type[i] === "closest") { |
| 623 | + let [closestSelector, remainingSelector = ""] = |
| 624 | + selectors[j].split(/\s+/, 2); |
| 625 | + queriedElement = |
| 626 | + queriedElement.closest(closestSelector); |
| 627 | + |
| 628 | + if (!queriedElement) continue; |
| 629 | + |
| 630 | + selectors[j] = remainingSelector; |
| 631 | + } |
| 632 | + |
572 | 633 | let specialSelectors = selectors[j].split(";"); |
573 | 634 | for (let k = 0; k < specialSelectors.length; k++) { |
574 | | - // TODO: Support an array of queried elements and branch off to return matches for each |
575 | | - // if (!Array.isArray(queriedElement)) { |
576 | | - // queriedElement = [queriedElement] |
577 | | - // } |
578 | | - |
| 635 | + specialSelectors[k] = specialSelectors[k].trim(); |
579 | 636 | if (!specialSelectors[k]) continue; |
580 | | - if (k === 0) { |
581 | | - if (type[i] === "parent") |
582 | | - queriedElement = queriedElement.parentElement; |
583 | | - else if (type[i] === "next") |
584 | | - queriedElement = |
585 | | - queriedElement.nextElementSibling; |
586 | | - else if (type[i] === "previous") |
587 | | - queriedElement = |
588 | | - queriedElement.previousElementSibling; |
589 | | - else if (type[i] === "document") |
590 | | - queriedElement = queriedElement.ownerDocument; |
591 | | - } else if (queriedElement.contentDocument) { |
592 | | - queriedElement = queriedElement.contentDocument; |
| 637 | + if (queryTypes.includes(specialSelectors[k])) { |
| 638 | + queriedElement = queryType( |
| 639 | + queriedElement, |
| 640 | + specialSelectors[k] |
| 641 | + ); |
| 642 | + continue; // Skip directly to the next specialSelector |
593 | 643 | } |
594 | 644 |
|
595 | | - switch ( |
596 | | - (specialSelectors[k] = specialSelectors[k].trim()) |
597 | | - ) { |
598 | | - case "top": |
599 | | - queriedElement = window.top.document; |
600 | | - break; |
601 | | - case "frame": |
602 | | - if (queriedElement.nodeType === 9) |
603 | | - queriedElement = |
604 | | - queriedElement.window.frameElement; |
605 | | - else if (queriedElement.contentDocument) |
606 | | - queriedElement = |
607 | | - queriedElement.contentDocument; |
608 | | - break; |
609 | | - case "document": |
610 | | - queriedElement = document; |
611 | | - break; |
612 | | - case "parent": |
613 | | - queriedElement = queriedElement.parentElement; |
614 | | - break; |
615 | | - case "next": |
616 | | - queriedElement = |
617 | | - queriedElement.nextElementSibling; |
618 | | - break; |
619 | | - case "previous": |
620 | | - queriedElement = |
621 | | - queriedElement.previousElementSibling; |
622 | | - break; |
623 | | - default: |
624 | | - if (k === 0 && type[i] === "closest") |
625 | | - if (specialSelectors[k].includes(" ")) { |
626 | | - let [firstSelector, ...restSelectors] = |
627 | | - specialSelectors[k].split(/ (.+)/); |
628 | | - queriedElement = |
629 | | - queriedElement.closest( |
630 | | - firstSelector |
631 | | - ); |
632 | | - if (restSelectors.length > 0) { |
633 | | - if (restSelectors[0].endsWith("[]")) |
634 | | - queriedElement = |
635 | | - queriedElement.querySelectorAll( |
636 | | - restSelectors[0].slice( |
637 | | - 0, |
638 | | - -2 |
639 | | - ) |
640 | | - ); |
641 | | - else |
642 | | - queriedElement = |
643 | | - queriedElement.querySelector( |
644 | | - restSelectors[0] |
645 | | - ); |
646 | | - } |
647 | | - } else { |
648 | | - // If no space, just use the selector with closest |
649 | | - queriedElement = queriedElement.closest( |
650 | | - specialSelectors[k] |
651 | | - ); |
652 | | - } |
653 | | - else if ( |
654 | | - specialSelectors[k] === "$clickedElement" |
655 | | - ) { |
656 | | - queriedElement = |
657 | | - queriedElement.clickedElement; |
658 | | - } else if (specialSelectors[k].endsWith("[]")) |
659 | | - queriedElement = |
660 | | - queriedElement.querySelectorAll( |
661 | | - specialSelectors[k].slice(0, -2) |
662 | | - ); |
663 | | - else |
664 | | - queriedElement = |
665 | | - queriedElement.querySelector( |
666 | | - specialSelectors[k] |
667 | | - ); |
| 645 | + if (queriedElement.contentDocument) { |
| 646 | + queriedElement = queriedElement.contentDocument; |
668 | 647 | } |
| 648 | + |
| 649 | + queriedElement = querySelector( |
| 650 | + queriedElement, |
| 651 | + specialSelectors[k] |
| 652 | + ); |
| 653 | + |
669 | 654 | if (!queriedElement) break; |
670 | 655 | } |
671 | 656 |
|
|
675 | 660 | queriedElement instanceof NodeList |
676 | 661 | ) { |
677 | 662 | for (let el of queriedElement) { |
678 | | - elements.set(el, ""); |
| 663 | + if (el instanceof Element) { |
| 664 | + elements.add(el); |
| 665 | + } |
679 | 666 | } |
680 | | - } else if (queriedElement) { |
681 | | - elements.set(queriedElement, ""); |
| 667 | + } else if (queriedElement instanceof Element) { |
| 668 | + elements.add(queriedElement); |
682 | 669 | } |
683 | 670 | } |
684 | | - } else if (Selector === "") { |
685 | | - if (type[i] === "parent" && element.parentElement) |
686 | | - elements.set(element.parentElement, ""); |
687 | | - else if (type[i] === "next" && element.nextElementSibling) |
688 | | - elements.set(element.nextElementSibling, ""); |
689 | | - else if ( |
690 | | - type[i] === "previous" && |
691 | | - element.previousElementSibling |
692 | | - ) |
693 | | - elements.set(element.previousElementSibling, ""); |
694 | 671 | } |
695 | 672 | } |
696 | 673 |
|
697 | 674 | if (!hasAttribute && !selector) { |
698 | 675 | elements = false; |
699 | | - } else elements = Array.from(elements.keys()); |
| 676 | + } else { |
| 677 | + elements = Array.from(elements); |
| 678 | + } |
700 | 679 |
|
701 | 680 | return elements; |
702 | 681 | } |
703 | 682 |
|
| 683 | + function queryType(element, type) { |
| 684 | + if (!element) return null; |
| 685 | + |
| 686 | + switch (type) { |
| 687 | + case "top": |
| 688 | + return window.top.document; |
| 689 | + case "frame": |
| 690 | + // ✅ If element is a document, return the iframe element containing it |
| 691 | + if (element.nodeType === 9) return window.frameElement; |
| 692 | + // ✅ If element is an iframe, return it as is |
| 693 | + return element; |
| 694 | + case "document": |
| 695 | + // ✅ If element is a document, return itself, else return `ownerDocument` |
| 696 | + return element.nodeType === 9 ? element : element.ownerDocument; |
| 697 | + case "parent": |
| 698 | + // ✅ If it's a document, return the parent document (if inside an iframe) |
| 699 | + if (element.nodeType === 9) { |
| 700 | + return element.defaultView !== window.top |
| 701 | + ? element.defaultView.parent.document |
| 702 | + : null; |
| 703 | + } |
| 704 | + // ✅ Otherwise, return parent element |
| 705 | + return element.parentElement; |
| 706 | + case "next": |
| 707 | + return element.nextElementSibling; |
| 708 | + case "previous": |
| 709 | + return element.previousElementSibling; |
| 710 | + default: |
| 711 | + return null; |
| 712 | + } |
| 713 | + } |
| 714 | + |
| 715 | + function querySelector(element, selector) { |
| 716 | + if (!element) return null; |
| 717 | + return selector.endsWith("[]") |
| 718 | + ? element.querySelectorAll(selector.slice(0, -2)) |
| 719 | + : element.querySelector(selector); |
| 720 | + } |
| 721 | + |
704 | 722 | const mediaRanges = { |
705 | 723 | xs: [0, 575], |
706 | 724 | sm: [576, 768], |
|
0 commit comments