@@ -102,42 +102,56 @@ function _followsInlineSpace(node: Node): boolean {
102102 * @returns {boolean }
103103 */
104104function _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 ) ;
108108 const isAtEdgeOfOwnSegment = _isBlockEdge ( node , side ) ;
109109 // In the DOM, a space before a BR is rendered but a space after a BR isn't.
110110 const isBeforeBR = side === 'end' && sibling && nodeName ( sibling ) === 'BR' ;
111111 return ( isAgainstAnotherSegment && ! isBeforeBR ) || isAtEdgeOfOwnSegment ;
112112}
113113/**
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.
118119 *
119120 * @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 }
122124 */
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 ) {
129142 // Look further (eg.: `<div><a>abc</a> <div>def</div></div>`: the
130143 // space should be removed).
131- let ancestor = node ;
132- while ( ancestor && ! ancestor [ siblingSide ] ) {
144+ let ancestor = lastExisting ;
145+ while ( ancestor && ! _significantSibling ( ancestor , direction , false ) ) {
133146 ancestor = ancestor . parentNode ;
134147 }
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 ) ;
138152 }
139- return cousin && _isSegment ( cousin ) ;
140153 }
154+ return sibling ;
141155}
142156/**
143157 * 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 {
175189
176190 // Return true if no ancestor up to the first block ancestor has a
177191 // sibling on the specified side
178- const siblingSide = side === 'start' ? 'previousSibling ' : 'nextSibling ' ;
192+ const direction = side === 'start' ? 'previous ' : 'next ' ;
179193 return ancestorsUpToBlock . every ( ancestor => {
180- let sibling = ancestor [ siblingSide ] ;
194+ let sibling = _significantSibling ( ancestor , direction ) ;
181195 while (
182196 sibling &&
183197 isInstanceOf ( sibling , Text ) &&
184198 sibling . textContent . match ( onlyTabsSpacesAndNewLines )
185199 ) {
186- sibling = sibling [ siblingSide ] ;
200+ sibling = _significantSibling ( sibling , direction ) ;
187201 }
188202 return ! sibling ;
189203 } ) ;
0 commit comments