0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2024-12-22 15:23:29 -05:00

Merge inlines when inserting node into range

This commit is contained in:
Neil Jenkins 2016-07-14 12:15:06 +10:00
parent 13241028f1
commit 766dea7ccf
5 changed files with 126 additions and 156 deletions

View file

@ -546,10 +546,7 @@ function split ( node, offset, stopNode, root ) {
return offset; return offset;
} }
function mergeInlines ( node, range ) { function _mergeInlines ( node, fakeRange ) {
if ( node.nodeType !== ELEMENT_NODE ) {
return;
}
var children = node.childNodes, var children = node.childNodes,
l = children.length, l = children.length,
frags = [], frags = [],
@ -559,30 +556,30 @@ function mergeInlines ( node, range ) {
prev = l && children[ l - 1 ]; prev = l && children[ l - 1 ];
if ( l && isInline( child ) && areAlike( child, prev ) && if ( l && isInline( child ) && areAlike( child, prev ) &&
!leafNodeNames[ child.nodeName ] ) { !leafNodeNames[ child.nodeName ] ) {
if ( range.startContainer === child ) { if ( fakeRange.startContainer === child ) {
range.startContainer = prev; fakeRange.startContainer = prev;
range.startOffset += getLength( prev ); fakeRange.startOffset += getLength( prev );
} }
if ( range.endContainer === child ) { if ( fakeRange.endContainer === child ) {
range.endContainer = prev; fakeRange.endContainer = prev;
range.endOffset += getLength( prev ); fakeRange.endOffset += getLength( prev );
} }
if ( range.startContainer === node ) { if ( fakeRange.startContainer === node ) {
if ( range.startOffset > l ) { if ( fakeRange.startOffset > l ) {
range.startOffset -= 1; fakeRange.startOffset -= 1;
} }
else if ( range.startOffset === l ) { else if ( fakeRange.startOffset === l ) {
range.startContainer = prev; fakeRange.startContainer = prev;
range.startOffset = getLength( prev ); fakeRange.startOffset = getLength( prev );
} }
} }
if ( range.endContainer === node ) { if ( fakeRange.endContainer === node ) {
if ( range.endOffset > l ) { if ( fakeRange.endOffset > l ) {
range.endOffset -= 1; fakeRange.endOffset -= 1;
} }
else if ( range.endOffset === l ) { else if ( fakeRange.endOffset === l ) {
range.endContainer = prev; fakeRange.endContainer = prev;
range.endOffset = getLength( prev ); fakeRange.endOffset = getLength( prev );
} }
} }
detach( child ); detach( child );
@ -598,14 +595,31 @@ function mergeInlines ( node, range ) {
while ( len-- ) { while ( len-- ) {
child.appendChild( frags.pop() ); child.appendChild( frags.pop() );
} }
mergeInlines( child, range ); _mergeInlines( child, fakeRange );
} }
} }
} }
function mergeInlines ( node, range ) {
if ( node.nodeType === TEXT_NODE ) {
node = node.parentNode;
}
if ( node.nodeType === ELEMENT_NODE ) {
var fakeRange = {
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
_mergeInlines( node, fakeRange );
range.setStart( fakeRange.startContainer, fakeRange.startOffset );
range.setEnd( fakeRange.endContainer, fakeRange.endOffset );
}
}
function mergeWithBlock ( block, next, range ) { function mergeWithBlock ( block, next, range ) {
var container = next, var container = next,
last, offset, _range; last, offset;
while ( container.parentNode.childNodes.length === 1 ) { while ( container.parentNode.childNodes.length === 1 ) {
container = container.parentNode; container = container.parentNode;
} }
@ -620,18 +634,11 @@ function mergeWithBlock ( block, next, range ) {
offset -= 1; offset -= 1;
} }
_range = {
startContainer: block,
startOffset: offset,
endContainer: block,
endOffset: offset
};
block.appendChild( empty( next ) ); block.appendChild( empty( next ) );
mergeInlines( block, _range );
range.setStart( _range.startContainer, _range.startOffset ); range.setStart( block, offset );
range.collapse( true ); range.collapse( true );
mergeInlines( block, range );
// Opera inserts a BR if you delete the last piece of text // Opera inserts a BR if you delete the last piece of text
// in a block-level element. Unfortunately, it then gets // in a block-level element. Unfortunately, it then gets
@ -886,6 +893,10 @@ var insertTreeFragmentIntoRange = function ( range, frag, root ) {
if ( allInline ) { if ( allInline ) {
// If inline, just insert at the current position. // If inline, just insert at the current position.
insertNodeInRange( range, frag ); insertNodeInRange( range, frag );
if ( range.startContainer !== range.endContainer ) {
mergeInlines( range.endContainer, range );
}
mergeInlines( range.startContainer, range );
range.collapse( false ); range.collapse( false );
} else { } else {
// Otherwise... // Otherwise...
@ -2691,12 +2702,7 @@ proto.getCursorPosition = function ( range ) {
rect = node.getBoundingClientRect(); rect = node.getBoundingClientRect();
parent = node.parentNode; parent = node.parentNode;
parent.removeChild( node ); parent.removeChild( node );
mergeInlines( parent, { mergeInlines( parent, range );
startContainer: range.startContainer,
endContainer: range.endContainer,
startOffset: range.startOffset,
endOffset: range.endOffset
});
} }
return rect; return rect;
}; };
@ -2969,38 +2975,31 @@ proto._getRangeAndRemoveBookmark = function ( range ) {
if ( start && end ) { if ( start && end ) {
var startContainer = start.parentNode, var startContainer = start.parentNode,
endContainer = end.parentNode, endContainer = end.parentNode,
collapsed; startOffset = indexOf.call( startContainer.childNodes, start ),
endOffset = indexOf.call( endContainer.childNodes, end );
var _range = {
startContainer: startContainer,
endContainer: endContainer,
startOffset: indexOf.call( startContainer.childNodes, start ),
endOffset: indexOf.call( endContainer.childNodes, end )
};
if ( startContainer === endContainer ) { if ( startContainer === endContainer ) {
_range.endOffset -= 1; endOffset -= 1;
} }
detach( start ); detach( start );
detach( end ); detach( end );
// Merge any text nodes we split
mergeInlines( startContainer, _range );
if ( startContainer !== endContainer ) {
mergeInlines( endContainer, _range );
}
if ( !range ) { if ( !range ) {
range = this._doc.createRange(); range = this._doc.createRange();
} }
range.setStart( _range.startContainer, _range.startOffset ); range.setStart( startContainer, startOffset );
range.setEnd( _range.endContainer, _range.endOffset ); range.setEnd( endContainer, endOffset );
collapsed = range.collapsed;
// Merge any text nodes we split
mergeInlines( startContainer, range );
if ( startContainer !== endContainer ) {
mergeInlines( endContainer, range );
}
// If we didn't split a text node, we should move into any adjacent // If we didn't split a text node, we should move into any adjacent
// text node to current selection point // text node to current selection point
if ( collapsed ) { if ( range.collapsed ) {
startContainer = range.startContainer; startContainer = range.startContainer;
if ( startContainer.nodeType === TEXT_NODE ) { if ( startContainer.nodeType === TEXT_NODE ) {
endContainer = startContainer.childNodes[ range.startOffset ]; endContainer = startContainer.childNodes[ range.startOffset ];
@ -3458,15 +3457,7 @@ proto._removeFormat = function ( tag, attributes, range, partial ) {
if ( fixer ) { if ( fixer ) {
range.collapse( false ); range.collapse( false );
} }
var _range = { mergeInlines( root, range );
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
mergeInlines( root, _range );
range.setStart( _range.startContainer, _range.startOffset );
range.setEnd( _range.endContainer, _range.endOffset );
return range; return range;
}; };
@ -4299,7 +4290,7 @@ proto.removeAllFormatting = function ( range ) {
var cleanNodes = doc.createDocumentFragment(); var cleanNodes = doc.createDocumentFragment();
var nodeAfterSplit = split( endContainer, endOffset, stopNode, root ); var nodeAfterSplit = split( endContainer, endOffset, stopNode, root );
var nodeInSplit = split( startContainer, startOffset, stopNode, root ); var nodeInSplit = split( startContainer, startOffset, stopNode, root );
var nextNode, _range, childNodes; var nextNode, childNodes;
// Then replace contents in split with a cleaned version of the same: // Then replace contents in split with a cleaned version of the same:
// blocks become default blocks, text and leaf nodes survive, everything // blocks become default blocks, text and leaf nodes survive, everything
@ -4326,15 +4317,9 @@ proto.removeAllFormatting = function ( range ) {
} }
// Merge text nodes at edges, if possible // Merge text nodes at edges, if possible
_range = { range.setStart( stopNode, startOffset );
startContainer: stopNode, range.setEnd( stopNode, endOffset );
startOffset: startOffset, mergeInlines( stopNode, range );
endContainer: stopNode,
endOffset: endOffset
};
mergeInlines( stopNode, _range );
range.setStart( _range.startContainer, _range.startOffset );
range.setEnd( _range.endContainer, _range.endOffset );
// And move back down the tree // And move back down the tree
moveRangeBoundariesDownTree( range ); moveRangeBoundariesDownTree( range );

File diff suppressed because one or more lines are too long

View file

@ -392,12 +392,7 @@ proto.getCursorPosition = function ( range ) {
rect = node.getBoundingClientRect(); rect = node.getBoundingClientRect();
parent = node.parentNode; parent = node.parentNode;
parent.removeChild( node ); parent.removeChild( node );
mergeInlines( parent, { mergeInlines( parent, range );
startContainer: range.startContainer,
endContainer: range.endContainer,
startOffset: range.startOffset,
endOffset: range.endOffset
});
} }
return rect; return rect;
}; };
@ -670,38 +665,31 @@ proto._getRangeAndRemoveBookmark = function ( range ) {
if ( start && end ) { if ( start && end ) {
var startContainer = start.parentNode, var startContainer = start.parentNode,
endContainer = end.parentNode, endContainer = end.parentNode,
collapsed; startOffset = indexOf.call( startContainer.childNodes, start ),
endOffset = indexOf.call( endContainer.childNodes, end );
var _range = {
startContainer: startContainer,
endContainer: endContainer,
startOffset: indexOf.call( startContainer.childNodes, start ),
endOffset: indexOf.call( endContainer.childNodes, end )
};
if ( startContainer === endContainer ) { if ( startContainer === endContainer ) {
_range.endOffset -= 1; endOffset -= 1;
} }
detach( start ); detach( start );
detach( end ); detach( end );
// Merge any text nodes we split
mergeInlines( startContainer, _range );
if ( startContainer !== endContainer ) {
mergeInlines( endContainer, _range );
}
if ( !range ) { if ( !range ) {
range = this._doc.createRange(); range = this._doc.createRange();
} }
range.setStart( _range.startContainer, _range.startOffset ); range.setStart( startContainer, startOffset );
range.setEnd( _range.endContainer, _range.endOffset ); range.setEnd( endContainer, endOffset );
collapsed = range.collapsed;
// Merge any text nodes we split
mergeInlines( startContainer, range );
if ( startContainer !== endContainer ) {
mergeInlines( endContainer, range );
}
// If we didn't split a text node, we should move into any adjacent // If we didn't split a text node, we should move into any adjacent
// text node to current selection point // text node to current selection point
if ( collapsed ) { if ( range.collapsed ) {
startContainer = range.startContainer; startContainer = range.startContainer;
if ( startContainer.nodeType === TEXT_NODE ) { if ( startContainer.nodeType === TEXT_NODE ) {
endContainer = startContainer.childNodes[ range.startOffset ]; endContainer = startContainer.childNodes[ range.startOffset ];
@ -1159,15 +1147,7 @@ proto._removeFormat = function ( tag, attributes, range, partial ) {
if ( fixer ) { if ( fixer ) {
range.collapse( false ); range.collapse( false );
} }
var _range = { mergeInlines( root, range );
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
mergeInlines( root, _range );
range.setStart( _range.startContainer, _range.startOffset );
range.setEnd( _range.endContainer, _range.endOffset );
return range; return range;
}; };
@ -2000,7 +1980,7 @@ proto.removeAllFormatting = function ( range ) {
var cleanNodes = doc.createDocumentFragment(); var cleanNodes = doc.createDocumentFragment();
var nodeAfterSplit = split( endContainer, endOffset, stopNode, root ); var nodeAfterSplit = split( endContainer, endOffset, stopNode, root );
var nodeInSplit = split( startContainer, startOffset, stopNode, root ); var nodeInSplit = split( startContainer, startOffset, stopNode, root );
var nextNode, _range, childNodes; var nextNode, childNodes;
// Then replace contents in split with a cleaned version of the same: // Then replace contents in split with a cleaned version of the same:
// blocks become default blocks, text and leaf nodes survive, everything // blocks become default blocks, text and leaf nodes survive, everything
@ -2027,15 +2007,9 @@ proto.removeAllFormatting = function ( range ) {
} }
// Merge text nodes at edges, if possible // Merge text nodes at edges, if possible
_range = { range.setStart( stopNode, startOffset );
startContainer: stopNode, range.setEnd( stopNode, endOffset );
startOffset: startOffset, mergeInlines( stopNode, range );
endContainer: stopNode,
endOffset: endOffset
};
mergeInlines( stopNode, _range );
range.setStart( _range.startContainer, _range.startOffset );
range.setEnd( _range.endContainer, _range.endOffset );
// And move back down the tree // And move back down the tree
moveRangeBoundariesDownTree( range ); moveRangeBoundariesDownTree( range );

View file

@ -368,10 +368,7 @@ function split ( node, offset, stopNode, root ) {
return offset; return offset;
} }
function mergeInlines ( node, range ) { function _mergeInlines ( node, fakeRange ) {
if ( node.nodeType !== ELEMENT_NODE ) {
return;
}
var children = node.childNodes, var children = node.childNodes,
l = children.length, l = children.length,
frags = [], frags = [],
@ -381,30 +378,30 @@ function mergeInlines ( node, range ) {
prev = l && children[ l - 1 ]; prev = l && children[ l - 1 ];
if ( l && isInline( child ) && areAlike( child, prev ) && if ( l && isInline( child ) && areAlike( child, prev ) &&
!leafNodeNames[ child.nodeName ] ) { !leafNodeNames[ child.nodeName ] ) {
if ( range.startContainer === child ) { if ( fakeRange.startContainer === child ) {
range.startContainer = prev; fakeRange.startContainer = prev;
range.startOffset += getLength( prev ); fakeRange.startOffset += getLength( prev );
} }
if ( range.endContainer === child ) { if ( fakeRange.endContainer === child ) {
range.endContainer = prev; fakeRange.endContainer = prev;
range.endOffset += getLength( prev ); fakeRange.endOffset += getLength( prev );
} }
if ( range.startContainer === node ) { if ( fakeRange.startContainer === node ) {
if ( range.startOffset > l ) { if ( fakeRange.startOffset > l ) {
range.startOffset -= 1; fakeRange.startOffset -= 1;
} }
else if ( range.startOffset === l ) { else if ( fakeRange.startOffset === l ) {
range.startContainer = prev; fakeRange.startContainer = prev;
range.startOffset = getLength( prev ); fakeRange.startOffset = getLength( prev );
} }
} }
if ( range.endContainer === node ) { if ( fakeRange.endContainer === node ) {
if ( range.endOffset > l ) { if ( fakeRange.endOffset > l ) {
range.endOffset -= 1; fakeRange.endOffset -= 1;
} }
else if ( range.endOffset === l ) { else if ( fakeRange.endOffset === l ) {
range.endContainer = prev; fakeRange.endContainer = prev;
range.endOffset = getLength( prev ); fakeRange.endOffset = getLength( prev );
} }
} }
detach( child ); detach( child );
@ -420,14 +417,31 @@ function mergeInlines ( node, range ) {
while ( len-- ) { while ( len-- ) {
child.appendChild( frags.pop() ); child.appendChild( frags.pop() );
} }
mergeInlines( child, range ); _mergeInlines( child, fakeRange );
} }
} }
} }
function mergeInlines ( node, range ) {
if ( node.nodeType === TEXT_NODE ) {
node = node.parentNode;
}
if ( node.nodeType === ELEMENT_NODE ) {
var fakeRange = {
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
_mergeInlines( node, fakeRange );
range.setStart( fakeRange.startContainer, fakeRange.startOffset );
range.setEnd( fakeRange.endContainer, fakeRange.endOffset );
}
}
function mergeWithBlock ( block, next, range ) { function mergeWithBlock ( block, next, range ) {
var container = next, var container = next,
last, offset, _range; last, offset;
while ( container.parentNode.childNodes.length === 1 ) { while ( container.parentNode.childNodes.length === 1 ) {
container = container.parentNode; container = container.parentNode;
} }
@ -442,18 +456,11 @@ function mergeWithBlock ( block, next, range ) {
offset -= 1; offset -= 1;
} }
_range = {
startContainer: block,
startOffset: offset,
endContainer: block,
endOffset: offset
};
block.appendChild( empty( next ) ); block.appendChild( empty( next ) );
mergeInlines( block, _range );
range.setStart( _range.startContainer, _range.startOffset ); range.setStart( block, offset );
range.collapse( true ); range.collapse( true );
mergeInlines( block, range );
// Opera inserts a BR if you delete the last piece of text // Opera inserts a BR if you delete the last piece of text
// in a block-level element. Unfortunately, it then gets // in a block-level element. Unfortunately, it then gets

View file

@ -202,6 +202,10 @@ var insertTreeFragmentIntoRange = function ( range, frag, root ) {
if ( allInline ) { if ( allInline ) {
// If inline, just insert at the current position. // If inline, just insert at the current position.
insertNodeInRange( range, frag ); insertNodeInRange( range, frag );
if ( range.startContainer !== range.endContainer ) {
mergeInlines( range.endContainer, range );
}
mergeInlines( range.startContainer, range );
range.collapse( false ); range.collapse( false );
} else { } else {
// Otherwise... // Otherwise...