-
Notifications
You must be signed in to change notification settings - Fork 878
Expand file tree
/
Copy pathis-in-text-block.js
More file actions
120 lines (103 loc) · 3.61 KB
/
is-in-text-block.js
File metadata and controls
120 lines (103 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import getComposedParent from './get-composed-parent';
import sanitize from '../text/sanitize';
import { getNodeFromTree, nodeLookup } from '../../core/utils';
import getRoleType from '../aria/get-role-type';
const blockLike = ['block', 'list-item', 'table', 'flex', 'grid'];
const inlineBlockLike = ['inline-block', 'inline-flex', 'inline-grid'];
/**
* Determines if an element is within a text block
* With `noLengthCompare` true, will return if there is any non-space text outside
* widgets. When false, compares the length of non-widget text to widget text
*
* @param {Element} node [description]
* @param {Object} options Optional
* @property {Bool} noLengthCompare
* @property {Bool} includeInlineBlock
* @return {Boolean} [description]
*/
function isInTextBlock(
node,
{ noLengthCompare, includeInlineBlock = false } = {}
) {
const { vNode, domNode } = nodeLookup(node);
if (isBlock(domNode) || (!includeInlineBlock && isInlineBlockLike(vNode))) {
// Ignore if the element is a block
return false;
}
// Find all the text part of the parent block not in a link, and all the text in a link
const virtualParent = getBlockParent(domNode);
let parentText = '';
let widgetText = '';
let inBrBlock = 0;
// We want to ignore hidden text, and if br / hr is used, only use the section of the parent
// that has the link we're looking at
walkDomNode(virtualParent, currNode => {
// We're already passed it, skip everything else
if (inBrBlock === 2) {
return false;
}
if (currNode.nodeType === 3) {
// Add the text to the parent
parentText += currNode.nodeValue;
}
// Ignore any node that's not an element (or text as above)
if (currNode.nodeType !== 1) {
return;
}
const nodeName = (currNode.nodeName || '').toUpperCase();
if (currNode === domNode) {
inBrBlock = 1;
}
// BR and HR elements break the line
if (['BR', 'HR'].includes(nodeName)) {
if (inBrBlock === 0) {
parentText = '';
widgetText = '';
} else {
inBrBlock = 2;
}
// Don't walk nodes with content not displayed on screen.
} else if (
currNode.style.display === 'none' ||
currNode.style.overflow === 'hidden' ||
!['', null, 'none'].includes(currNode.style.float) ||
!['', null, 'relative'].includes(currNode.style.position)
) {
return false;
// Don't walk widgets, we're only interested in what's not in them.
} else if (getRoleType(currNode) === 'widget') {
// Grab all the text from this element, but don't walk down it's children
widgetText += currNode.textContent;
return false;
}
});
parentText = sanitize(parentText);
if (noLengthCompare) {
return parentText.length !== 0;
}
widgetText = sanitize(widgetText);
return parentText.length > widgetText.length;
}
export default isInTextBlock;
function isBlock(node) {
const { vNode } = nodeLookup(node);
const display = vNode.getComputedStylePropertyValue('display');
return blockLike.includes(display) || display.substr(0, 6) === 'table-';
}
function isInlineBlockLike(vNode) {
const display = vNode.getComputedStylePropertyValue('display');
return inlineBlockLike.includes(display);
}
function walkDomNode(node, functor) {
if (functor(node.actualNode) !== false) {
node.children.forEach(child => walkDomNode(child, functor));
}
}
function getBlockParent(node) {
// Find the closest parent
let parentBlock = getComposedParent(node);
while (parentBlock && !isBlock(parentBlock)) {
parentBlock = getComposedParent(parentBlock);
}
return getNodeFromTree(parentBlock);
}