/**
 * Utilities that manipulate or query the DOM tree.
 * @author Louis-Dominique Dubeau
 * @license MPL 2.0
 * @copyright Mangalam Research Center for Buddhist Languages
 */
import { isAttr } from "./domtypeguards";
/**
 * Search an array.
 *
 * @param a The array to search.
 *
 * @param target The target to find.
 *
 * @return -1 if the target is not found, or its index.
 */
export declare function indexOf(a: NodeList | HTMLCollection, target: Node): number;
export declare function indexOf<T>(a: T[], target: T): number;
/**
 * Compare two positions in document order.
 *
 * This function relies on DOM's ``compareDocumentPosition`` function. Remember
 * that calling that function with attributes can be problematic. (For instance,
 * two attributes on the same element are not ordered.)
 *
 * @param firstNode Node of the first position.
 *
 * @param firstOffset Offset of the first position.
 *
 * @param secondNode Node of the second position.
 *
 * @param secondOffset Offset of the second position.
 *
 * @returns -1 if the first position comes before the second. 1 if the first
 * position comes after the other. 0 if the two positions are equal.
 */
export declare function comparePositions(firstNode: Node, firstOffset: number, secondNode: Node, secondOffset: number): 1 | 0 | -1;
/**
 * Gets the first range in the selection.
 *
 * @param win The window for which we want the selection.
 *
 * @returns The first range in the selection. Undefined if there is no selection
 * or no range.
 */
export declare function getSelectionRange(win: Window): Range | undefined;
/**
 * A range and a flag indicating whether it is a reversed range or not. Range
 * objects themselves do not record how they were created. If the range was
 * created from a starting point which is greater than the end point (in
 * document order), then the range is "reversed".
 */
export declare type RangeInfo = {
    range: Range;
    reversed: boolean;
};
/**
 * Creates a range from two points in a document.
 *
 * @returns The range information.
 */
export declare function rangeFromPoints(startContainer: Node, startOffset: number, endContainer: Node, endOffset: number): RangeInfo;
/**
 * Focuses the node itself or if the node is a text node, focuses the parent.
 *
 * @param node The node to focus.
 *
 * @throws {Error} If the node is neither a text node nor an element. Trying to
 * focus something other than these is almost certainly an algorithmic bug.
 */
export declare function focusNode(node: Node): void;
/**
 * A caret position in the form of a pair of values. The caret we are talking
 * about here roughly corresponds to the caret that a "contenteditable" element
 * would present to the user. It can index in text nodes and element nodes but
 * not in attributes.
 */
export declare type Caret = [Node, number];
/**
 * This function determines the caret position if the caret was moved forward.
 *
 * This function does not fully emulate how a browser moves the caret. The sole
 * emulation it performs is to check whether whitespace matters or not. It skips
 * whitespace that does not matter.
 *
 * @param caret A caret position where the search starts. This should be an
 * array of length two that has in first position the node where the caret is
 * and in second position the offset in that node. This pair is to be
 * interpreted in the same way node, offset pairs are interpreted in selection
 * or range objects.
 *
 * @param container A DOM node which indicates the container within which caret
 * movements must be contained.
 *
 * @param noText If true, and a text node would be returned, the function will
 * instead return the parent of the text node.
 *
 * @returns The next caret position, or ``null`` if such position does not
 * exist. The ``container`` parameter constrains movements to positions inside
 * it.
 */
export declare function nextCaretPosition(caret: Caret, container: Node, noText: boolean): Caret | null;
/**
 * This function determines the caret position if the caret was moved backwards.
 *
 * This function does not fully emulate how a browser moves the caret. The sole
 * emulation it performs is to check whether whitespace matters or not. It skips
 * whitespace that does not matter.
 *
 * @param caret A caret position where the search starts. This should be an
 * array of length two that has in first position the node where the caret is
 * and in second position the offset in that node. This pair is to be
 * interpreted in the same way node, offset pairs are interpreted in selection
 * or range objects.
 *
 * @param container A DOM node which indicates the container within which caret
 * movements must be contained.
 *
 * @param noText If true, and a text node would be returned, the function will
 * instead return the parent of the text node.
 *
 * @returns The previous caret position, or ``null`` if such position does not
 * exist. The ``container`` parameter constrains movements to positions inside
 * it.
 */
export declare function prevCaretPosition(caret: Caret, container: Node, noText: boolean): Caret | null;
/**
 * Given two trees A and B of DOM nodes, this function finds the node in tree B
 * which corresponds to a node in tree A. The two trees must be structurally
 * identical. If tree B is cloned from tree A, it will satisfy this
 * requirement. This function does not work with attribute nodes.
 *
 * @param treeA The root of the first tree.
 *
 * @param treeB The root of the second tree.
 *
 * @param nodeInA A node in the first tree.
 *
 * @returns The node which corresponds to ``nodeInA`` in ``treeB``.
 *
 * @throws {Error} If ``nodeInA`` is not ``treeA`` or a child of ``treeA``.
 */
export declare function correspondingNode(treeA: Node, treeB: Node, nodeInA: Node): Node;
/**
 * Makes a placeholder element
 *
 * @param text The text to put in the placeholder.
 *
 * @returns A node.
 */
export declare function makePlaceholder(text?: string): HTMLElement;
export declare type InsertionBoundaries = [Caret, Caret];
export interface GenericInsertIntoTextContext {
    insertNodeAt(into: Element, index: number, node: Node): void;
    deleteNode(node: Node): void;
    /**
     * This function performs roughly the same as insertNodeAt but may be
     * optimized to take care of fragment handling.
     */
    insertFragAt?(into: Element, index: number, node: DocumentFragment): void;
}
/**
 * Inserts an element into text, effectively splitting the text node in
 * two. This function takes care to modify the DOM tree only once.
 *
 * @param textNode The text node that will be cut in two by the new element.
 *
 * @param index The offset into the text node where the new element is to be
 * inserted.
 *
 * @param node The node to insert.
 *
 * @returns A pair containing a caret position marking the boundary between what
 * comes before the material inserted and the material inserted, and a caret
 * position marking the boundary between the material inserted and what comes
 * after. If I insert "foo" at position 2 in "abcd", then the final result would
 * be "abfoocd" and the first caret would mark the boundary between "ab" and
 * "foo" and the second caret the boundary between "foo" and "cd".
 *
 * @throws {Error} If the node to insert is undefined or null.
 */
export declare function genericInsertIntoText(this: GenericInsertIntoTextContext, textNode: Text, index: number, node?: Node): InsertionBoundaries;
/**
 * Records the results of inserting text into the tree.
 */
export interface TextInsertionResult {
    /** The node that contains the added text. */
    node: Text | undefined;
    /** Whether [[node]] is a new node. If ``false``, it was modified. */
    isNew: boolean;
    /** The caret position after the insertion. */
    caret: Caret;
}
export interface GenericInsertTextContext {
    insertNodeAt(into: Element, index: number, node: Node): void;
    setTextNodeValue(node: Text, value: string): void;
}
/**
 * Inserts text into a node. This function will use already existing
 * text nodes whenever possible rather than create a new text node.
 *
 * @param node The node where the text is to be inserted.
 *
 * @param index The location in the node where the text is
 * to be inserted.
 *
 * @param text The text to insert.
 *
 * @param caretAtEnd Whether the caret position returned should be placed at the
 * end of the inserted text.
 *
 * @returns The result of inserting the text.
 *
 * @throws {Error} If ``node`` is not an element or text Node type.
 */
export declare function genericInsertText(this: GenericInsertTextContext, node: Node, index: number, text: string, caretAtEnd?: boolean): TextInsertionResult;
/**
 * Deletes text from a text node. If the text node becomes empty, it is deleted.
 *
 * @param node The text node from which to delete text.
 *
 * @param index The index at which to delete text.
 *
 * @param length The length of text to delete.
 *
 * @throws {Error} If ``node`` is not a text Node type.
 */
export declare function deleteText(node: Text, index: number, length: number): void;
/**
 * This function recursively links two DOM trees through the jQuery ``.data()``
 * method. For an element in the first tree the data item named
 * "wed_mirror_node" points to the corresponding element in the second tree, and
 * vice-versa. It is presumed that the two DOM trees are perfect mirrors of each
 * other, although no test is performed to confirm this.
 */
export declare function linkTrees(rootA: Element, rootB: Element): void;
/**
 * This function recursively unlinks a DOM tree though the jQuery ``.data()``
 * method.
 *
 * @param root A DOM node.
 *
 */
export declare function unlinkTree(root: Element): void;
/**
 * Returns the first descendant or the node passed to the function if the node
 * happens to not have a descendant. The function searches in document order.
 *
 * When passed ``<p><b>A</b><b><q>B</q></b></p>`` this code would return the
 * text node "A" because it has no children and is first.
 *
 * @param node The node to search.
 *
 * @returns The first node which is both first in its parent and has no
 * children.
 */
export declare function firstDescendantOrSelf(node: Node | null | undefined): Node | null;
/**
 * Returns the last descendant or the node passed to the function if the node
 * happens to not have a descendant. The function searches in reverse document
 * order.
 *
 * When passed ``<p><b>A</b><b><q>B</q></b></p>`` this code would return the
 * text node "B" because it has no children and is last.
 *
 * @param node The node to search.
 *
 * @returns The last node which is both last in its parent and has no
 * children.
 */
export declare function lastDescendantOrSelf(node: Node | null | undefined): Node | null;
/**
 * Removes the node. Mainly for use with the generic functions defined here.
 *
 * @param node The node to remove.
 */
export declare function deleteNode(node: Node): void;
/**
 * Inserts text into a node. This function will use already existing text nodes
 * whenever possible rather than create a new text node.
 *
 * @function
 *
 * @param node The node where the text is to be inserted.
 *
 * @param index The location in the node where the text is to be inserted.
 *
 * @param text The text to insert.
 *
 * @param caretAtEnd Whether to return the caret position at the end of the
 * inserted text or at the beginning. Default to ``true``.
 *
 * @returns The result of inserting the text.
 *
 * @throws {Error} If ``node`` is not an element or text Node type.
 */
export declare function insertText(node: Node, index: number, text: string, caretAtEnd?: boolean): TextInsertionResult;
/**
 * Inserts an element into text, effectively splitting the text node in
 * two. This function takes care to modify the DOM tree only once.
 *
 * @param textNode The text node that will be cut in two by the new element.
 *
 * @param index The offset into the text node where the new element is to be
 * inserted.
 *
 * @param node The node to insert.
 *
 * @returns A pair containing a caret position marking the boundary between what
 * comes before the material inserted and the material inserted, and a caret
 * position marking the boundary between the material inserted and what comes
 * after. If I insert "foo" at position 2 in "abcd", then the final result would
 * be "abfoocd" and the first caret would mark the boundary between "ab" and
 * "foo" and the second caret the boundary between "foo" and "cd".
 */
export declare function insertIntoText(textNode: Text, index: number, node: Node): InsertionBoundaries;
export declare type SplitResult = [Node, Node];
/**
 * Splits a text node into two nodes. This function takes care to modify the DOM
 * tree only once.
 *
 * @param textNode The text node to split into two text nodes.
 *
 * @param index The offset into the text node where to split.
 *
 * @returns The first element is the node before index after split and the
 * second element is the node after the index after split.
 */
export declare function splitTextNode(textNode: Text, index: number): SplitResult;
/**
 * Merges a text node with the next text node, if present. When called on
 * something which is not a text node or if the next node is not text, does
 * nothing. Mainly for use with the generic functions defined here.
 *
 * @param node The node to merge with the next node.
 *
 * @returns A caret position between the two parts that were merged, or between
 * the two nodes that were not merged (because they were not both text).
 */
export declare function mergeTextNodes(node: Node): Caret;
export interface RangeLike {
    startContainer: Node;
    startOffset: number;
    endContainer: Node;
    endOffset: number;
}
export declare type ElementPair = [Element, Element];
/**
 * Determines whether a range is well-formed. A well-formed range is one which
 * starts and ends in the same element.
 *
 * @param range An object which has the ``startContainer``,
 * ``startOffset``, ``endContainer``, ``endOffset`` attributes set. The
 * interpretation of these values is the same as for DOM ``Range``
 * objects. Therefore, the object passed can be a DOM range.
 *
 * @returns ``true`` if the range is well-formed.  ``false`` if not.
 */
export declare function isWellFormedRange(range: RangeLike): boolean;
export interface GenericCutContext {
    deleteText(node: Text, index: number, length: number): void;
    deleteNode(node: Node): void;
    mergeTextNodes(node: Node): void;
}
export declare type CutResult = [Caret, Node[]];
/**
 * Removes the contents between the start and end carets from the DOM tree. If
 * two text nodes become adjacent, they are merged.
 *
 * @param startCaret Start caret position.
 *
 * @param endCaret Ending caret position.
 *
 * @returns The first item is the caret position indicating where the cut
 * happened. The second item is a list of nodes, the cut contents.
 *
 * @throws {Error} If Nodes in the range are not in the same element.
 */
export declare function genericCutFunction(this: GenericCutContext, startCaret: Caret, endCaret: Caret): CutResult;
/**
 * Copies a well formed region of the DOM tree.
 *
 * @param startCaret Start caret position.
 *
 * @param endCaret Ending caret position.
 *
 * @returns A copy of the contents.
 *
 * @throws {Error} If Nodes in the range are not in the same element.
 */
export declare function copy(startCaret: Caret, endCaret: Caret): Node[];
/**
 * Dumps a range to the console.
 *
 * @param msg A message to output in front of the range information.
 *
 * @param range The range.
 */
export declare function dumpRange(msg: string, range?: RangeLike): void;
/**
 * Dumps the current selection to the console.
 *
 * @param msg A message to output in front of the range information.
 *
 * @param win The window for which to dump selection information.
 */
export declare function dumpCurrentSelection(msg: string, win: Window): void;
/**
 * Dumps a range to a string.
 *
 * @param msg A message to output in front of the range information.
 *
 * @param range The range.
 */
export declare function dumpRangeToString(msg: string, range?: RangeLike): string;
/**
 * Checks whether a point is in the element's contents. This means inside the
 * element and **not** inside one of the scrollbars that the element may
 * have. The coordinates passed must be **relative to the document.** If the
 * coordinates are taken from an event, this means passing ``pageX`` and
 * ``pageY``.
 *
 * @param element The element to check.
 *
 * @param x The x coordinate **relative to the document.**
 *
 * @param y The y coordinate **relative to the document.**
 *
 * @returns ``true`` if inside, ``false`` if not.
 */
export declare function pointInContents(element: Element, x: number, y: number): boolean;
/**
 * Starting with the node passed, and walking up the node's
 * parents, returns the first node that matches the selector.
 *
 * @param node The node to start with.
 *
 * @param selector The selector to use for matches.
 *
 * @param limit The algorithm will search up to this limit, inclusively.
 *
 * @returns The first element that matches the selector, or ``null`` if nothing
 * matches.
 */
export declare function closest(node: Node | undefined | null, selector: string, limit?: Element | Document): Element | null;
/**
 * Starting with the node passed, and walking up the node's parents, returns the
 * first element that matches the class.
 *
 * @param node The node to start with.
 *
 * @param cl The class to use for matches.
 *
 * @param limit The algorithm will search up to this limit, inclusively.
 *
 * @returns The first element that matches the class, or ``null`` if nothing
 * matches.
 */
export declare function closestByClass(node: Node | undefined | null, cl: string, limit?: Element | Document): Element | null;
/**
 * Find a sibling matching the class.
 *
 * @param node The element whose sibling we are looking for.
 *
 * @param cl The class to use for matches.
 *
 * @returns The first sibling (in document order) that matches the class, or
 * ``null`` if nothing matches.
 */
export declare function siblingByClass(node: Node | null, cl: string): Element | null;
/**
 * Find children matching the class.
 *
 * @param node The element whose children we are looking for.
 *
 * @param cl The class to use for matches.
 *
 * @returns The children (in document order) that match the class.
 */
export declare function childrenByClass(node: Node | null, cl: string): Element[];
/**
 * Find child matching the class.
 *
 * @param node The element whose child we are looking for.
 *
 * @param cl The class to use for matches.
 *
 * @returns The first child (in document order) that matches the class, or
 * ``null`` if nothing matches.
 */
export declare function childByClass(node: Node | null, cl: string): Element | null;
/**
 * Convert a string to HTML encoding. For instance if you want to have the
 * less-than symbol be part of the contents of a ``span`` element, it would have
 * to be escaped to ``<`` otherwise it would be interpreted as the beginning of
 * a tag. This function does this kind of escaping.
 *
 * @param text The text to convert.
 *
 * @returns The converted text.
 */
export declare function textToHTML(text: string): string;
/**
 * Converts a CSS selector written as if it were run against the XML document
 * being edited by wed into a selector that will match the corresponding items
 * in the GUI tree. This implementation is extremely naive and likely to break
 * on complex selectors. Some specific things it cannot do:
 *
 * - Match attributes.
 *
 * - Match pseudo-elements.
 *
 * @param selector The selector to convert.
 *
 * @param namespaces The namespaces that are known. This is used to convert
 * element name prefixes to namespace URIs.
 *
 * @returns The converted selector.
 */
export declare function toGUISelector(selector: string, namespaces: Record<string, string>): string;
/**
 * Allows applying simple CSS selectors on the data tree as if it were an HTML
 * tree. This is necessary because the current browsers are unable to handle tag
 * prefixes or namespaces in selectors passed to ``matches``, ``querySelector``
 * and related functions.
 *
 * The steps are:
 *
 * 1. Convert ``selector`` with [[toGUISelector]] into a selector that can be
 * applied to the GUI tree.
 *
 * 2. Convert ``node`` to a GUI node.
 *
 * 3. Apply the converted selector to the GUI node.
 *
 * 4. Convert the resulting node to a data node.
 *
 * @param node The element to use as the starting point of the query.
 *
 * @param selector The selector to use.
 *
 * @param namespaces The namespaces that are known. This is used to convert
 * element name prefixes to namespace URIs.
 *
 * @returns The resulting data node.
 */
export declare function dataFind(node: Element, selector: string, namespaces: Record<string, string>): Element | null;
/**
 * Allows applying simple CSS selectors on the data tree as if it were an HTML
 * tree. Operates like [[dataFind]] but returns an array of nodes.
 *
 * @param node The data node to use as the starting point of the query.
 *
 * @param selector The selector to use.
 *
 * @param namespaces The namespaces that are known. This is used to convert
 * element name prefixes to namespace URIs.
 *
 * @returns The resulting data nodes.
 */
export declare function dataFindAll(node: Element, selector: string, namespaces: Record<string, string>): Element[];
/**
 * Converts an HTML string to an array of DOM nodes. **This function is not
 * responsible for checking the HTML for security holes it is the responsibility
 * of the calling code to ensure the HTML passed is clean.**
 *
 * @param html The HTML to convert.
 *
 * @param document The document for which to create the nodes. If not specified,
 * the document will be the global ``document``.
 *
 * @returns The resulting nodes.
 */
export declare function htmlToElements(html: string, document?: Document): Node[];
/**
 * Gets the character immediately before the caret. The word "immediately" here
 * means that this function does not walk the DOM. If the caret is pointing into
 * an element node, it will check whether the node before the offset is a text
 * node and use it. That's the extent to which it walks the DOM.
 *
 * @param caret The caret position.
 *
 * @return The character, if it exists.
 */
export declare function getCharacterImmediatelyBefore(caret: Caret): string | undefined;
/**
 * Gets the character immediately at the caret. The word "immediately" here
 * means that this function does not walk the DOM. If the caret is pointing into
 * an element node, it will check whether the node at the offset is a text
 * node and use it. That's the extent to which it walks the DOM.
 *
 * @param caret The caret position.
 *
 * @return The character, if it exists.
 */
export declare function getCharacterImmediatelyAt(caret: Caret): string | undefined;
/**
 * Determine whether an element is displayed. This function is designed to
 * handle checks in wed's GUI tree, and not as a general purpose solution. It
 * only checks whether the element or its parents have ``display`` set to
 * ``"none"``.
 *
 * @param el The DOM element for which we want to check whether it is displayed
 * or not.
 *
 * @param root The parent of ``el`` beyond which we do not search.
 *
 * @returns ``true`` if the element or any of its parents is not
 * displayed. ``false`` otherwise. If the search up the DOM tree hits ``root``,
 * then the value returned is ``false``.
 */
export declare function isNotDisplayed(el: HTMLElement, root: HTMLElement | HTMLDocument): boolean;
/**
 * A ``contains`` function that handles attributes. Attributes are not part of
 * the node tree and performing a ``contains`` test on them is always ``false``.
 *
 * Yet it makes sense to say that an element A contains its own attributes and
 * thus by transitivity if element A is contained by element B, then all
 * attributes of A are contained by B. This function supports the contention
 * just described.
 *
 * Usage note: this function is typically not *needed* when doing tests in the
 * GUI tree because we do not address attributes in that tree. There is,
 * however, no harm in using it where it is not strictly needed. In the data
 * tree, however, we do address attributes. Code that works with either tree
 * (e.g. the [["wed/dloc"]] module) should use this function as a general rule
 * so that it can work with either tree.
 *
 * @param container The thing which should contain in the test.
 *
 * @param contained The thing which should be contained in the test.
 *
 * @returns Whether ``container`` contains ``contained``.
 */
export declare function contains(container: Node, contained: Node): boolean;
export { isAttr };
