@@ -102,42 +102,56 @@ function _followsInlineSpace(node: Node): boolean {
102
102
* @returns {boolean }
103
103
*/
104
104
function _isAtSegmentBreak ( node : Node , side : 'start' | 'end' ) : boolean {
105
- const siblingSide = side === 'start' ? 'previousSibling ' : 'nextSibling ' ;
106
- const sibling = node && node [ siblingSide ] ;
107
- const isAgainstAnotherSegment = _isAgainstAnotherSegment ( node , side ) ;
105
+ const direction = side === 'start' ? 'previous ' : 'next ' ;
106
+ const sibling = _significantSibling ( node , direction ) ;
107
+ const isAgainstAnotherSegment = sibling && _isSegment ( sibling ) ;
108
108
const isAtEdgeOfOwnSegment = _isBlockEdge ( node , side ) ;
109
109
// In the DOM, a space before a BR is rendered but a space after a BR isn't.
110
110
const isBeforeBR = side === 'end' && sibling && nodeName ( sibling ) === 'BR' ;
111
111
return ( isAgainstAnotherSegment && ! isBeforeBR ) || isAtEdgeOfOwnSegment ;
112
112
}
113
113
/**
114
- * Return true if the given node is just before or just after another segment.
115
- * Eg: <div>abc<div>def</div></div> -> abc is before another segment (div).
116
- * Eg: <div><a>abc</a> <div>def</div></div> -> abc is before another segment
117
- * (div).
114
+ * Return the next|previous "significant" node. Nodes to skip are empty text
115
+ * nodes (text nodes with no text content or only whitespace), and hidden
116
+ * inputs.
117
+ * If `searchUp` is true (which is the default), the next|previous "significant"
118
+ * node may be a cousin.
118
119
*
119
120
* @param {Node } node
120
- * @param {'start'|'end' } side
121
- * @returns {boolean }
121
+ * @param {'previous'|'next' } direction
122
+ * @param {boolean } [searchUp] (default: true)
123
+ * @returns {Node }
122
124
*/
123
- function _isAgainstAnotherSegment ( node : Node , side : 'start' | 'end' ) : boolean {
124
- const siblingSide = side === 'start' ? 'previousSibling' : 'nextSibling' ;
125
- const sibling = node && node [ siblingSide ] ;
126
- if ( sibling ) {
127
- return sibling && _isSegment ( sibling ) ;
128
- } else {
125
+ function _significantSibling ( node : Node , direction : 'previous' | 'next' , searchUp = true ) : Node {
126
+ const siblingSide = direction === 'previous' ? 'previousSibling' : 'nextSibling' ;
127
+ let sibling : Node = node && node [ siblingSide ] ;
128
+ let lastExisting = sibling || node ;
129
+ if (
130
+ sibling &&
131
+ // Sibling is an empty text node.
132
+ ( ( sibling . nodeType === Node . TEXT_NODE &&
133
+ onlyTabsSpacesAndNewLines . test ( sibling . textContent ) ) ||
134
+ // Sibling is a hidden input.
135
+ ( nodeName ( sibling ) === 'INPUT' &&
136
+ ( sibling as Element ) . getAttribute ( 'type' ) === 'hidden' ) )
137
+ ) {
138
+ lastExisting = sibling ;
139
+ sibling = sibling [ siblingSide ] ;
140
+ }
141
+ if ( ! sibling && searchUp ) {
129
142
// Look further (eg.: `<div><a>abc</a> <div>def</div></div>`: the
130
143
// space should be removed).
131
- let ancestor = node ;
132
- while ( ancestor && ! ancestor [ siblingSide ] ) {
144
+ let ancestor = lastExisting ;
145
+ while ( ancestor && ! _significantSibling ( ancestor , direction , false ) ) {
133
146
ancestor = ancestor . parentNode ;
134
147
}
135
- let cousin = ancestor && ! _isSegment ( ancestor ) && ancestor . nextSibling ;
136
- while ( cousin && isInstanceOf ( cousin , Text ) ) {
137
- cousin = cousin . nextSibling ;
148
+ sibling =
149
+ ancestor && ! _isSegment ( ancestor ) && _significantSibling ( ancestor , direction , false ) ;
150
+ while ( sibling && isInstanceOf ( sibling , Text ) ) {
151
+ sibling = _significantSibling ( sibling , direction , false ) ;
138
152
}
139
- return cousin && _isSegment ( cousin ) ;
140
153
}
154
+ return sibling ;
141
155
}
142
156
/**
143
157
* Return true if the node is a segment according to W3 formatting model.
@@ -175,15 +189,15 @@ function _isBlockEdge(node: Node, side: 'start' | 'end'): boolean {
175
189
176
190
// Return true if no ancestor up to the first block ancestor has a
177
191
// sibling on the specified side
178
- const siblingSide = side === 'start' ? 'previousSibling ' : 'nextSibling ' ;
192
+ const direction = side === 'start' ? 'previous ' : 'next ' ;
179
193
return ancestorsUpToBlock . every ( ancestor => {
180
- let sibling = ancestor [ siblingSide ] ;
194
+ let sibling = _significantSibling ( ancestor , direction ) ;
181
195
while (
182
196
sibling &&
183
197
isInstanceOf ( sibling , Text ) &&
184
198
sibling . textContent . match ( onlyTabsSpacesAndNewLines )
185
199
) {
186
- sibling = sibling [ siblingSide ] ;
200
+ sibling = _significantSibling ( sibling , direction ) ;
187
201
}
188
202
return ! sibling ;
189
203
} ) ;
0 commit comments