mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
refactor(mobile): Move querySelector polyfill to @jitsi/js-utils package
Moves the querySelector/querySelectorAll polyfill implementation from the local codebase to the @jitsi/js-utils package (v2.6.1) to reduce code duplication and make the polyfill available to other projects. This centralizes maintenance and ensures consistent behavior across the Jitsi ecosystem.
This commit is contained in:
15
package-lock.json
generated
15
package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"@giphy/react-components": "6.9.4",
|
||||
"@giphy/react-native-sdk": "4.1.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/js-utils": "2.6.7",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/rnnoise-wasm": "0.2.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
@@ -4544,9 +4544,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jitsi/js-utils": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.2.1.tgz",
|
||||
"integrity": "sha512-4Ia4hWO7aTMGbYftzeBr+IHIu5YxiWwTlhsSK34z6925oNAUNI863WgYYGTcXkW/1yuM6LBZrnuZBySDqosISA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.6.7.tgz",
|
||||
"integrity": "sha512-r16J3CjYt325CFIpfHznND4O3b9BE7CZ7cu+Xx0uk1C+ZY6/bDPZFM/0d4t0VH+1/rmfG4I7i18qxXct3xmPrw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"js-md5": "0.7.3",
|
||||
@@ -30016,9 +30017,9 @@
|
||||
"integrity": "sha512-8fAv3cVEuoukSyu5RBZg0YWcrGEMjSKsdeQJuMmeOL2vVXIBWo0TpaHqys4HNCGRmZKzkhYccqxtmNSTxlBgkQ=="
|
||||
},
|
||||
"@jitsi/js-utils": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.2.1.tgz",
|
||||
"integrity": "sha512-4Ia4hWO7aTMGbYftzeBr+IHIu5YxiWwTlhsSK34z6925oNAUNI863WgYYGTcXkW/1yuM6LBZrnuZBySDqosISA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.6.7.tgz",
|
||||
"integrity": "sha512-r16J3CjYt325CFIpfHznND4O3b9BE7CZ7cu+Xx0uk1C+ZY6/bDPZFM/0d4t0VH+1/rmfG4I7i18qxXct3xmPrw==",
|
||||
"requires": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"js-md5": "0.7.3",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@giphy/react-components": "6.9.4",
|
||||
"@giphy/react-native-sdk": "4.1.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/js-utils": "2.6.7",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/rnnoise-wasm": "0.2.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { querySelector, querySelectorAll } from '@jitsi/js-utils/polyfills/querySelectorPolyfill';
|
||||
import { DOMParser } from '@xmldom/xmldom';
|
||||
import { atob, btoa } from 'abab';
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
@@ -8,7 +9,6 @@ import 'promise.withresolvers/auto'; // Promise.withResolvers.
|
||||
import 'react-native-url-polyfill/auto'; // Complete URL polyfill.
|
||||
|
||||
import Storage from './Storage';
|
||||
import { querySelector, querySelectorAll } from './querySelectorPolyfill';
|
||||
|
||||
const { AppInfo } = NativeModules;
|
||||
|
||||
@@ -193,7 +193,7 @@ const { AppInfo } = NativeModules;
|
||||
// - lib-jitsi-meet -> XMLUtils.ts -> parseXML
|
||||
if (typeof document.querySelector === 'undefined') {
|
||||
document.querySelector = function(selectors) {
|
||||
return this.documentElement ? querySelector(this.documentElement, selectors) : null;
|
||||
return querySelector(this, selectors);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ const { AppInfo } = NativeModules;
|
||||
// - lib-jitsi-meet -> XMLUtils.ts -> parseXML
|
||||
if (typeof document.querySelectorAll === 'undefined') {
|
||||
document.querySelectorAll = function(selectors) {
|
||||
return this.documentElement ? querySelectorAll(this.documentElement, selectors) : [];
|
||||
return querySelectorAll(this, selectors);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ const { AppInfo } = NativeModules;
|
||||
// - lib-jitsi-meet -> XMLUtils.ts -> parseXML
|
||||
if (typeof documentPrototype.querySelector === 'undefined') {
|
||||
documentPrototype.querySelector = function(selectors) {
|
||||
return this.documentElement ? querySelector(this.documentElement, selectors) : null;
|
||||
return querySelector(this, selectors);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ const { AppInfo } = NativeModules;
|
||||
// - lib-jitsi-meet -> XMLUtils.ts -> parseXML
|
||||
if (typeof documentPrototype.querySelectorAll === 'undefined') {
|
||||
documentPrototype.querySelectorAll = function(selectors) {
|
||||
return this.documentElement ? querySelectorAll(this.documentElement, selectors) : [];
|
||||
return querySelectorAll(this, selectors);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,282 +0,0 @@
|
||||
// Regex constants for efficient reuse across selector parsing
|
||||
const SIMPLE_TAG_NAME_REGEX = /^[a-zA-Z][\w-]*$/;
|
||||
const MULTI_ATTRIBUTE_SELECTOR_REGEX = /^([a-zA-Z][\w-]*)?(\[(?:\*\|)?([^=\]]+)=["']?([^"'\]]+)["']?\])+$/;
|
||||
const SINGLE_ATTRIBUTE_REGEX = /\[(?:\*\|)?([^=\]]+)=["']?([^"'\]]+)["']?\]/g;
|
||||
const WHITESPACE_AROUND_COMBINATOR_REGEX = /\s*>\s*/g;
|
||||
|
||||
/**
|
||||
* Parses a CSS selector into reusable components.
|
||||
*
|
||||
* @param {string} selector - The CSS selector to parse.
|
||||
* @returns {Object} - Object with tagName and attrConditions properties.
|
||||
*/
|
||||
function _parseSelector(selector) {
|
||||
// Wildcard selector
|
||||
if (selector === '*') {
|
||||
return {
|
||||
tagName: null, // null means match all tag names
|
||||
attrConditions: []
|
||||
};
|
||||
}
|
||||
|
||||
// Simple tag name
|
||||
if (SIMPLE_TAG_NAME_REGEX.test(selector)) {
|
||||
return {
|
||||
tagName: selector,
|
||||
attrConditions: []
|
||||
};
|
||||
}
|
||||
|
||||
// Attribute selector: tagname[attr="value"] or
|
||||
// tagname[attr1="value1"][attr2="value2"] (with optional wildcard namespace)
|
||||
const multiAttrMatch = selector.match(MULTI_ATTRIBUTE_SELECTOR_REGEX);
|
||||
|
||||
if (multiAttrMatch) {
|
||||
const tagName = multiAttrMatch[1];
|
||||
const attrConditions = [];
|
||||
let attrMatch;
|
||||
|
||||
while ((attrMatch = SINGLE_ATTRIBUTE_REGEX.exec(selector)) !== null) {
|
||||
attrConditions.push({
|
||||
name: attrMatch[1], // This properly strips the *| prefix
|
||||
value: attrMatch[2]
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
tagName,
|
||||
attrConditions
|
||||
};
|
||||
}
|
||||
|
||||
// Unsupported selector
|
||||
throw new SyntaxError(`Unsupported selector pattern: '${selector}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters elements by selector pattern and handles findFirst logic.
|
||||
*
|
||||
* @param {Element[]} elements - Array of elements to filter.
|
||||
* @param {string} selector - CSS selector to match against.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Filtered results with proper return type.
|
||||
*/
|
||||
function _filterAndMatchElements(elements, selector, findFirst) {
|
||||
const { tagName, attrConditions } = _parseSelector(selector);
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const element of elements) {
|
||||
// Check tag name if specified
|
||||
if (tagName && !(element.localName === tagName || element.tagName === tagName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if all attribute conditions match
|
||||
const allMatch = attrConditions.every(condition =>
|
||||
element.getAttribute(condition.name) === condition.value
|
||||
);
|
||||
|
||||
if (allMatch) {
|
||||
results.push(element);
|
||||
if (findFirst) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return findFirst ? null : results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles direct child traversal for selectors with > combinators.
|
||||
* This is the shared logic used by both scope selectors and regular direct child selectors.
|
||||
*
|
||||
* @param {Element[]} startElements - Array of starting elements to traverse from.
|
||||
* @param {string[]} selectorParts - Array of selector parts split by '>'.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Array of Elements for querySelectorAll,
|
||||
* single Element or null for querySelector.
|
||||
*/
|
||||
function _traverseDirectChildren(startElements, selectorParts, findFirst) {
|
||||
let currentElements = startElements;
|
||||
|
||||
for (const part of selectorParts) {
|
||||
const nextElements = [];
|
||||
|
||||
currentElements.forEach(el => {
|
||||
// Get direct children
|
||||
const directChildren = Array.from(el.children || []);
|
||||
|
||||
// Use same helper as handlers
|
||||
const matchingChildren = _filterAndMatchElements(directChildren, part, false);
|
||||
|
||||
nextElements.push(...matchingChildren);
|
||||
});
|
||||
|
||||
currentElements = nextElements;
|
||||
|
||||
// If we have no results, we can stop early (applies to both querySelector and querySelectorAll)
|
||||
if (currentElements.length === 0) {
|
||||
return findFirst ? null : [];
|
||||
}
|
||||
}
|
||||
|
||||
return findFirst ? currentElements[0] || null : currentElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles :scope pseudo-selector cases with direct child combinators.
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selector - The CSS selector.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Array of Elements for querySelectorAll,
|
||||
* single Element or null for querySelector.
|
||||
*/
|
||||
function _handleScopeSelector(node, selector, findFirst) {
|
||||
let searchSelector = selector.substring(6);
|
||||
|
||||
// Handle :scope > tagname (direct children)
|
||||
if (searchSelector.startsWith('>')) {
|
||||
searchSelector = searchSelector.substring(1);
|
||||
|
||||
// Split by > and use shared traversal logic
|
||||
const parts = searchSelector.split('>');
|
||||
|
||||
// Start from the node itself (scope)
|
||||
return _traverseDirectChildren([ node ], parts, findFirst);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles nested > selectors (direct child combinators).
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selector - The CSS selector.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Array of Elements for querySelectorAll,
|
||||
* single Element or null for querySelector.
|
||||
*/
|
||||
function _handleDirectChildSelectors(node, selector, findFirst) {
|
||||
const parts = selector.split('>');
|
||||
|
||||
// First find elements matching the first part (this could be descendants, not just direct children)
|
||||
const startElements = _querySelectorInternal(node, parts[0], false);
|
||||
|
||||
// If no starting elements found, return early
|
||||
if (startElements.length === 0) {
|
||||
return findFirst ? null : [];
|
||||
}
|
||||
|
||||
// Use shared traversal logic for the remaining parts
|
||||
return _traverseDirectChildren(startElements, parts.slice(1), findFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles simple tag name selectors.
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selector - The CSS selector.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Array of Elements for querySelectorAll,
|
||||
* single Element or null for querySelector.
|
||||
*/
|
||||
function _handleSimpleTagSelector(node, selector, findFirst) {
|
||||
const elements = Array.from(node.getElementsByTagName(selector));
|
||||
|
||||
if (findFirst) {
|
||||
return elements[0] || null;
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles attribute selectors: tagname[attr="value"] or tagname[attr1="value1"][attr2="value2"].
|
||||
* Supports single or multiple attributes with optional wildcard namespace (*|).
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selector - The CSS selector.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Array of Elements for querySelectorAll,
|
||||
* single Element or null for querySelector.
|
||||
*/
|
||||
function _handleAttributeSelector(node, selector, findFirst) {
|
||||
const { tagName } = _parseSelector(selector); // Just to get tagName for optimization
|
||||
|
||||
// Handler's job: find the right elements to search
|
||||
const elementsToCheck = tagName
|
||||
? Array.from(node.getElementsByTagName(tagName))
|
||||
: Array.from(node.getElementsByTagName('*'));
|
||||
|
||||
// Common helper does the matching
|
||||
return _filterAndMatchElements(elementsToCheck, selector, findFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function that implements the core selector matching logic for both
|
||||
* querySelector and querySelectorAll. Supports :scope pseudo-selector, direct
|
||||
* child selectors, and common CSS selectors.
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selector - The CSS selector to match elements against.
|
||||
* @param {boolean} findFirst - If true, return after finding the first match.
|
||||
* @returns {Element[]|Element|null} - Array of Elements for querySelectorAll,
|
||||
* single Element or null for querySelector.
|
||||
*/
|
||||
function _querySelectorInternal(node, selector, findFirst = false) {
|
||||
// Normalize whitespace around > combinators first
|
||||
const normalizedSelector = selector.replace(WHITESPACE_AROUND_COMBINATOR_REGEX, '>');
|
||||
|
||||
// Handle :scope pseudo-selector
|
||||
if (normalizedSelector.startsWith(':scope')) {
|
||||
return _handleScopeSelector(node, normalizedSelector, findFirst);
|
||||
}
|
||||
|
||||
// Handle nested > selectors (direct child combinators)
|
||||
if (normalizedSelector.includes('>')) {
|
||||
return _handleDirectChildSelectors(node, normalizedSelector, findFirst);
|
||||
}
|
||||
|
||||
// Fast path: simple tag name
|
||||
if (normalizedSelector === '*' || SIMPLE_TAG_NAME_REGEX.test(normalizedSelector)) {
|
||||
return _handleSimpleTagSelector(node, normalizedSelector, findFirst);
|
||||
}
|
||||
|
||||
// Attribute selector: tagname[attr="value"] or
|
||||
// tagname[attr1="value1"][attr2="value2"] (with optional wildcard namespace)
|
||||
if (normalizedSelector.match(MULTI_ATTRIBUTE_SELECTOR_REGEX)) {
|
||||
return _handleAttributeSelector(node, normalizedSelector, findFirst);
|
||||
}
|
||||
|
||||
// Unsupported selector - throw SyntaxError to match browser behavior
|
||||
throw new SyntaxError(`Failed to execute 'querySelector${
|
||||
findFirst ? '' : 'All'}' on 'Element': '${selector}' is not a valid selector.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements querySelector functionality using the shared internal logic.
|
||||
* Supports the same selectors as querySelectorAll but returns only the first match.
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selectors - The CSS selector to match elements against.
|
||||
* @returns {Element|null} - The first Element which matches the selector, or null.
|
||||
*/
|
||||
export function querySelector(node, selectors) {
|
||||
return _querySelectorInternal(node, selectors, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements querySelectorAll functionality using the shared internal logic.
|
||||
* Supports :scope pseudo-selector, direct child selectors, and common CSS selectors.
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selector - The CSS selector to match elements against.
|
||||
* @returns {Element[]} - Array of Elements matching the selector.
|
||||
*/
|
||||
export function querySelectorAll(node, selector) {
|
||||
return _querySelectorInternal(node, selector, false);
|
||||
}
|
||||
Reference in New Issue
Block a user