UNPKG

3.82 kBPlain TextView Raw
1/**
2 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS-IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import { assert } from "./assertions";
18
19/**
20 * Checks if the node is the root of a document. This is either a Document
21 * or ShadowRoot. DocumentFragments are included for simplicity of the
22 * implementation, though we only want to consider Documents or ShadowRoots.
23 * @param node The node to check.
24 * @return True if the node the root of a document, false otherwise.
25 */
26function isDocumentRoot(node: Node): node is Document | ShadowRoot {
27 return node.nodeType === 11 || node.nodeType === 9;
28}
29
30/**
31 * Checks if the node is an Element. This is faster than an instanceof check.
32 * @param node The node to check.
33 * @return Whether or not the node is an Element.
34 */
35function isElement(node: Node): node is Element {
36 return node.nodeType === 1;
37}
38
39/**
40 * Checks if the node is a text node. This is faster than an instanceof check.
41 * @param node The node to check.
42 * @return Whether or not the node is a Text.
43 */
44function isText(node: Node): node is Text {
45 return node.nodeType === 3;
46}
47
48/**
49 * @param node The node to start at, inclusive.
50 * @param root The root ancestor to get until, exclusive.
51 * @return The ancestry of DOM nodes.
52 */
53function getAncestry(node: Node, root: Node | null) {
54 const ancestry: Array<Node> = [];
55 let cur: Node | null = node;
56
57 while (cur !== root) {
58 const n: Node = assert(cur);
59 ancestry.push(n);
60 cur = n.parentNode;
61 }
62
63 return ancestry;
64}
65
66/**
67 * @param this
68 * @returns The root node of the DOM tree that contains this node.
69 */
70const getRootNode =
71 (typeof Node !== "undefined" && (Node as any).prototype.getRootNode) ||
72 function(this: Node) {
73 let cur: Node | null = this as Node;
74 let prev = cur;
75
76 while (cur) {
77 prev = cur;
78 cur = cur.parentNode;
79 }
80
81 return prev;
82 };
83
84/**
85 * @param node The node to get the activeElement for.
86 * @returns The activeElement in the Document or ShadowRoot
87 * corresponding to node, if present.
88 */
89function getActiveElement(node: Node): Element | null {
90 const root = getRootNode.call(node);
91 return isDocumentRoot(root) ? root.activeElement : null;
92}
93
94/**
95 * Gets the path of nodes that contain the focused node in the same document as
96 * a reference node, up until the root.
97 * @param node The reference node to get the activeElement for.
98 * @param root The root to get the focused path until.
99 * @returns The path of focused parents, if any exist.
100 */
101function getFocusedPath(node: Node, root: Node | null): Array<Node> {
102 const activeElement = getActiveElement(node);
103
104 if (!activeElement || !node.contains(activeElement)) {
105 return [];
106 }
107
108 return getAncestry(activeElement, root);
109}
110
111/**
112 * Like insertBefore, but instead instead of moving the desired node, instead
113 * moves all the other nodes after.
114 * @param parentNode
115 * @param node
116 * @param referenceNode
117 */
118function moveBefore(parentNode: Node, node: Node, referenceNode: Node | null) {
119 const insertReferenceNode = node.nextSibling;
120 let cur = referenceNode;
121
122 while (cur !== null && cur !== node) {
123 const next = cur.nextSibling;
124 parentNode.insertBefore(cur, insertReferenceNode);
125 cur = next;
126 }
127}
128
129export { isElement, isText, getFocusedPath, moveBefore };