• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

worktile / slate-angular / 543eb906-9e4d-47ae-b0a3-bf4f680c78ba

pending completion
543eb906-9e4d-47ae-b0a3-bf4f680c78ba

push

circleci

Maple13
build: optimizing angular 15 upgrade

267 of 876 branches covered (30.48%)

Branch coverage included in aggregate %.

682 of 1485 relevant lines covered (45.93%)

30.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

21.57
/packages/src/utils/dom.ts
1
/**
2
 * Types.
3
 */
4

5
// COMPAT: This is required to prevent TypeScript aliases from doing some very
6
// weird things for Slate's types with the same name as globals. (2019/11/27)
7
// https://github.com/microsoft/TypeScript/issues/35002
8
import DOMNode = globalThis.Node;
1✔
9
import DOMComment = globalThis.Comment;
1✔
10
import DOMElement = globalThis.Element;
1✔
11
import DOMText = globalThis.Text;
1✔
12
import DOMRange = globalThis.Range;
1✔
13
import DOMSelection = globalThis.Selection;
1✔
14
import DOMStaticRange = globalThis.StaticRange;
1✔
15
export {
16
    DOMNode,
17
    DOMComment,
18
    DOMElement,
19
    DOMText,
20
    DOMRange,
21
    DOMSelection,
22
    DOMStaticRange
23
};
24

25
declare global {
26
    interface Window {
27
        Selection: typeof Selection['constructor'];
28
        DataTransfer: typeof DataTransfer['constructor'];
29
        Node: typeof Node['constructor'];
30
    }
31
}
32

33
export type DOMPoint = [Node, number];
34

35
/**
36
 * Returns the host window of a DOM node
37
 */
38
export const getDefaultView = (value: any): Window | null => {
1✔
39
    return (
24✔
40
        (value && value.ownerDocument && value.ownerDocument.defaultView) || null
72!
41
    );
42
}
43

44
/**
45
 * Check if a DOM node is a comment node.
46
 */
47
export const isDOMComment = (value: any): value is DOMComment => {
1✔
48
    return isDOMNode(value) && value.nodeType === 8;
×
49
};
50

51
/**
52
 * Check if a DOM node is an element node.
53
 */
54
export const isDOMElement = (value: any): value is DOMElement => {
1✔
55
    return isDOMNode(value) && value.nodeType === 1;
8✔
56
};
57

58
/**
59
 * Check if a value is a DOM node.
60
 */
61
export const isDOMNode = (value: any): value is DOMNode => {
1✔
62
    const window = getDefaultView(value)
10✔
63
    return !!window && value instanceof window.Node;
10✔
64
};
65

66
/**
67
 * Check if a value is a DOM selection.
68
 */
69
export const isDOMSelection = (value: any): value is DOMSelection => {
1✔
70
    const window = value && value.anchorNode && getDefaultView(value.anchorNode)
×
71
    return !!window && value instanceof window.Selection
×
72
}
73

74
/**
75
 * Check if a DOM node is an element node.
76
 */
77
export const isDOMText = (value: any): value is DOMText => {
1✔
78
    return isDOMNode(value) && value.nodeType === 3;
×
79
};
80

81
/**
82
 * Checks whether a paste event is a plaintext-only event.
83
 */
84
export const isPlainTextOnlyPaste = (event: ClipboardEvent) => {
1✔
85
    return (
×
86
        event.clipboardData &&
×
87
        event.clipboardData.getData('text/plain') !== '' &&
88
        event.clipboardData.types.length === 1
89
    );
90
};
91

92
/**
93
 * Normalize a DOM point so that it always refers to a text node.
94
 */
95
export const normalizeDOMPoint = (domPoint: DOMPoint): DOMPoint => {
1✔
96
    let [node, offset] = domPoint;
×
97

98
    // If it's an element node, its offset refers to the index of its children
99
    // including comment nodes, so try to find the right text child node.
100
    if (isDOMElement(node) && node.childNodes.length) {
×
101
        let isLast = offset === node.childNodes.length;
×
102
        let index = isLast ? offset - 1 : offset;
×
103
        ;[node, index] = getEditableChildAndIndex(node,
×
104
            index,
105
            isLast ? 'backward' : 'forward');
×
106

107
        // If the editable child found is in front of input offset, we instead seek to its end
108
        isLast = index < offset;
×
109

110
        // If the node has children, traverse until we have a leaf node. Leaf nodes
111
        // can be either text nodes, or other void DOM nodes.
112
        while (isDOMElement(node) && node.childNodes.length) {
×
113
            const i = isLast ? node.childNodes.length - 1 : 0;
×
114
            node = getEditableChild(node, i, isLast ? 'backward' : 'forward');
×
115
        }
116

117
        // Determine the new offset inside the text node.
118
        offset =
×
119
            isLast && node.textContent != null ? node.textContent.length : 0;
×
120
    }
121

122
    // Return the node and offset.
123
    return [node, offset];
×
124
};
125

126
/**
127
 * Determines wether the active element is nested within a shadowRoot
128
 */
129

130
export const hasShadowRoot = () => {
1✔
131
    return !!(
×
132
        window.document.activeElement && window.document.activeElement.shadowRoot
×
133
    )
134
}
135

136
/**
137
 * Get the nearest editable child and index at `index` in a `parent`, preferring
138
 * `direction`.
139
 */
140

141
export const getEditableChildAndIndex = (
1✔
142
    parent: DOMElement,
143
    index: number,
144
    direction: 'forward' | 'backward'
145
): [DOMNode, number] => {
146
    const { childNodes } = parent;
×
147
    let child = childNodes[index];
×
148
    let i = index;
×
149
    let triedForward = false;
×
150
    let triedBackward = false;
×
151

152
    // While the child is a comment node, or an element node with no children,
153
    // keep iterating to find a sibling non-void, non-comment node.
154
    while (
×
155
        isDOMComment(child) ||
×
156
        (isDOMElement(child) && child.childNodes.length === 0) ||
157
        (isDOMElement(child) && child.getAttribute('contenteditable') === 'false')
158
    ) {
159
        if (triedForward && triedBackward) {
×
160
            break;
×
161
        }
162

163
        if (i >= childNodes.length) {
×
164
            triedForward = true;
×
165
            i = index - 1;
×
166
            direction = 'backward';
×
167
            continue;
×
168
        }
169

170
        if (i < 0) {
×
171
            triedBackward = true;
×
172
            i = index + 1;
×
173
            direction = 'forward';
×
174
            continue;
×
175
        }
176

177
        child = childNodes[i];
×
178
        index = i;
×
179
        i += direction === 'forward' ? 1 : -1;
×
180
    }
181

182
    return [child, index];
×
183
}
184

185
/**
186
 * Get the nearest editable child at `index` in a `parent`, preferring
187
 * `direction`.
188
 */
189

190
export const getEditableChild = (
1✔
191
    parent: DOMElement,
192
    index: number,
193
    direction: 'forward' | 'backward'
194
): DOMNode => {
195
    const [child] = getEditableChildAndIndex(parent, index, direction);
×
196
    return child;
×
197
}
198

199
/**
200
 * Get a plaintext representation of the content of a node, accounting for block
201
 * elements which get a newline appended.
202
 *
203
 * The domNode must be attached to the DOM.
204
 */
205
export const getPlainText = (domNode: DOMNode) => {
1✔
206
    let text = '';
×
207

208
    if (isDOMText(domNode) && domNode.nodeValue) {
×
209
        return domNode.nodeValue;
×
210
    }
211

212
    if (isDOMElement(domNode)) {
×
213
        for (const childNode of Array.from(domNode.childNodes)) {
×
214
            text += getPlainText(childNode);
×
215
        }
216

217
        const display = getComputedStyle(domNode).getPropertyValue('display');
×
218

219
        if (display === 'block' || display === 'list' || domNode.tagName === 'BR') {
×
220
            text += '\n';
×
221
        }
222
    }
223

224
    return text;
×
225
};
226

227
/**
228
 * Get x-slate-fragment attribute from data-slate-fragment
229
 */
230
const catchSlateFragment = /data-slate-fragment="(.+?)"/m
1✔
231
export const getSlateFragmentAttribute = (
1✔
232
    dataTransfer: DataTransfer
233
): string | void => {
234
    const htmlData = dataTransfer.getData('text/html')
×
235
    const [, fragment] = htmlData.match(catchSlateFragment) || []
×
236
    return fragment
×
237
}
238

239
/**
240
 * Get the x-slate-fragment attribute that exist in text/html data
241
 * and append it to the DataTransfer object
242
 */
243
export const getClipboardData = (dataTransfer: DataTransfer, clipboardFormatKey = 'x-slate-fragment'): DataTransfer => {
1!
244
    if (!dataTransfer.getData(`application/${clipboardFormatKey}`)) {
×
245
        const fragment = getSlateFragmentAttribute(dataTransfer)
×
246
        if (fragment) {
×
247
            const clipboardData = new DataTransfer()
×
248
            dataTransfer.types.forEach(type => {
×
249
                clipboardData.setData(type, dataTransfer.getData(type))
×
250
            })
251
            clipboardData.setData(`application/${clipboardFormatKey}`, fragment)
×
252
            return clipboardData
×
253
        }
254
    }
255
    return dataTransfer
×
256
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc