UNPKG

lisn.js

Version:

Simply handle user gestures and actions. Includes widgets.

178 lines (173 loc) 7.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitForReferenceElement = exports.getReferenceElement = void 0; var MH = _interopRequireWildcard(require("../globals/minification-helpers.cjs")); var _cssAlter = require("./css-alter.cjs"); var _domEvents = require("./dom-events.cjs"); var _log = require("./log.cjs"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * @module Utils * * @categoryDescription DOM: Searching for reference elements * The functions allow you to find elements that match a given string * specification. */ /** * Get the element that matches the given reference specification. * * The specification is of the form: * * ``` * <FullSpec> ::= * <Relation> "." <ClassName> | * <Relation> ["-" <ReferenceName>] | * #<DOM_ID> * * <Relation> :== * "next" | * "prev" | * "this" | * "first" | * "last" * ``` * * - `<DOM_ID>` is the unique ID of the element * - `<ClassName>` is a CSS class on the element * - `<ReferenceName>` is the value of the `data-lisn-ref` attribute on the * element you are targeting. If not given, defaults to the value of the * `data-lisn-ref` attribute on `thisElement`. * * There now follows an explanation of how "next", "prev", "this", "first" and * "last" search the DOM: * - "next": the tree is search in document order (depth first, then breadth), * so the returned element could be a descendant of `thisElement` * - "prev": the tree is search in document order (depth first, then breadth), * but excluding ancestors of `thisElement`, so the returned element is * guaranteed to _not_ be an ancestor or descendant of `thisElement`. * - "this": if the given element itself matches the specification, it is * returned, otherwise the closest ancestor of the given element that matches * the specification * - "first": the first element matching; the tree is search in document order * (depth first, then breadth). * - "last": the last element matching; the tree is search in document order * (depth first, then breadth). * * @category DOM: Searching for reference elements * * @param thisElement The element to search relative to * * @throws {@link Errors.LisnUsageError | LisnUsageError} * If the specification is invalid or if thisElement is * not given for a specification of "next", "prev" or "this" */ const getReferenceElement = (spec, thisElement) => { if (!spec) { return thisElement; } if (spec[0] === "#") { // element ID const referenceElement = MH.getElementById(spec.slice(1)); if (!referenceElement) { return null; } return referenceElement; } const relation = ["next", "prev", "this", "first", "last"].find(p => spec.startsWith(`${p}.`) || spec.startsWith(`${p}-`) || spec === p); if (!relation) { throw MH.usageError(`Invalid search specification '${spec}'`); } const rest = spec.slice(MH.lengthOf(relation)); const matchOp = rest.slice(0, 1); let refOrCls = rest.slice(1); let selector; if (matchOp === ".") { selector = matchOp + refOrCls; } else { if (!refOrCls) { var _getData; refOrCls = (_getData = (0, _cssAlter.getData)(thisElement, PREFIX_REF)) !== null && _getData !== void 0 ? _getData : ""; } if (!refOrCls) { throw MH.usageError(`No reference name in '${spec}'`); } selector = `[${DATA_REF}="${refOrCls}"]`; } let referenceElement; if (relation === "first") { referenceElement = getFirstReferenceElement(selector); } else if (relation === "last") { referenceElement = getLastReferenceElement(selector); } else { if (relation === "this") { referenceElement = getThisReferenceElement(selector, thisElement); } else if (relation === "next") { referenceElement = getNextReferenceElement(selector, thisElement); } else if (relation === "prev") { referenceElement = getPrevReferenceElement(selector, thisElement); } else { /* istanbul ignore next */{ (0, _log.logError)(MH.bugError(`Unhandled relation case ${relation}`)); return null; } } } if (!referenceElement) { return null; } return referenceElement; }; /** * Like {@link getReferenceElement} excepts if no element matches the * specification if will wait for at most the given time for such an element. * * @category DOM: Searching for reference elements */ exports.getReferenceElement = getReferenceElement; const waitForReferenceElement = (spec, thisElement, timeout = 200) => (0, _domEvents.waitForElement)(() => getReferenceElement(spec, thisElement), timeout); // ---------------------------------------- exports.waitForReferenceElement = waitForReferenceElement; const PREFIX_REF = MH.prefixName("ref"); const DATA_REF = MH.prefixData(PREFIX_REF); const getAllReferenceElements = selector => MH.docQuerySelectorAll(selector); const getFirstReferenceElement = selector => MH.docQuerySelector(selector); const getLastReferenceElement = selector => { const allRefs = getAllReferenceElements(selector); return allRefs && allRefs[MH.lengthOf(allRefs) - 1] || null; }; const getThisReferenceElement = (selector, thisElement) => thisElement.closest(selector); const getNextReferenceElement = (selector, thisElement) => getNextOrPrevReferenceElement(selector, thisElement, false); const getPrevReferenceElement = (selector, thisElement) => getNextOrPrevReferenceElement(selector, thisElement, true); const getNextOrPrevReferenceElement = (selector, thisElement, goBackward) => { var _getThisReferenceElem, _allRefs$refIndex; thisElement = (_getThisReferenceElem = getThisReferenceElement(selector, thisElement)) !== null && _getThisReferenceElem !== void 0 ? _getThisReferenceElem : thisElement; if (!MH.getDoc().contains(thisElement)) { return null; } const allRefs = getAllReferenceElements(selector); if (!allRefs) { return null; } const numRefs = MH.lengthOf(allRefs); let refIndex = goBackward ? numRefs - 1 : -1; for (let i = 0; i < numRefs; i++) { const currentIsAfter = MH.isNodeBAfterA(thisElement, allRefs[i]); // As soon as we find either the starting element or the first element // that follows it, stop iteration. // - If we're looking for the previous reference, then take the previous // element in the iteration. // - Otherwise, if the current element in the iteration is the same as the // starting one, then take either the next element in the iteration. // - Otherwise, (if the current element follows the starting one, as // would happen if the starting element was not in the list of matched // elements, take the current element in the iteration. if (allRefs[i] === thisElement || currentIsAfter) { refIndex = i + (goBackward ? -1 : currentIsAfter ? 0 : 1); break; } } return (_allRefs$refIndex = allRefs[refIndex]) !== null && _allRefs$refIndex !== void 0 ? _allRefs$refIndex : null; }; //# sourceMappingURL=dom-search.cjs.map