mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 07:13:08 -05:00
Move cursor outside of <a> when inserting HTML
When links are pasted into the editor the cursor ends up at the end of the text node inside the parent <a> element. Any text entered is then appended to the end of the link text. Chrome automatically moves the cursor after the end of <a> elements when additional text is inserted, so this change enforces the same behaviour in other browsers. Resolves LP 55607264 https://app.liquidplanner.com/space/14822/projects/show/55607264
This commit is contained in:
parent
8c6e52c120
commit
bb3cd05c64
5 changed files with 54 additions and 23 deletions
|
@ -1096,6 +1096,9 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( true ) {
|
while ( true ) {
|
||||||
|
if ( endContainer === endMax || endContainer === root ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if ( maySkipBR &&
|
if ( maySkipBR &&
|
||||||
endContainer.nodeType !== TEXT_NODE &&
|
endContainer.nodeType !== TEXT_NODE &&
|
||||||
endContainer.childNodes[ endOffset ] &&
|
endContainer.childNodes[ endOffset ] &&
|
||||||
|
@ -1103,9 +1106,7 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
||||||
endOffset += 1;
|
endOffset += 1;
|
||||||
maySkipBR = false;
|
maySkipBR = false;
|
||||||
}
|
}
|
||||||
if ( endContainer === endMax ||
|
if ( endOffset !== getLength( endContainer ) ) {
|
||||||
endContainer === root ||
|
|
||||||
endOffset !== getLength( endContainer ) ) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parent = endContainer.parentNode;
|
parent = endContainer.parentNode;
|
||||||
|
@ -1117,6 +1118,20 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
||||||
range.setEnd( endContainer, endOffset );
|
range.setEnd( endContainer, endOffset );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var moveRangeBoundaryOutOf = function ( range, nodeName, root ) {
|
||||||
|
var parent = getNearest( range.endContainer, root, 'A' );
|
||||||
|
if ( parent ) {
|
||||||
|
var clone = range.cloneRange();
|
||||||
|
parent = parent.parentNode;
|
||||||
|
moveRangeBoundariesUpTree( clone, parent, parent, root );
|
||||||
|
if ( clone.endContainer === parent ) {
|
||||||
|
range.setStart( clone.endContainer, clone.endOffset );
|
||||||
|
range.setEnd( clone.endContainer, clone.endOffset );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
// Returns the first block at least partially contained by the range,
|
// Returns the first block at least partially contained by the range,
|
||||||
// or null if no block is contained by the range.
|
// or null if no block is contained by the range.
|
||||||
var getStartBlockOfRange = function ( range, root ) {
|
var getStartBlockOfRange = function ( range, root ) {
|
||||||
|
@ -1468,12 +1483,7 @@ var handleEnter = function ( self, shiftKey, range ) {
|
||||||
// just play it safe and insert a <br>.
|
// just play it safe and insert a <br>.
|
||||||
if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) {
|
if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) {
|
||||||
// If inside an <a>, move focus out
|
// If inside an <a>, move focus out
|
||||||
parent = getNearest( range.endContainer, root, 'A' );
|
moveRangeBoundaryOutOf( range, 'A', root );
|
||||||
if ( parent ) {
|
|
||||||
parent = parent.parentNode;
|
|
||||||
moveRangeBoundariesUpTree( range, parent, parent, root );
|
|
||||||
range.collapse( false );
|
|
||||||
}
|
|
||||||
insertNodeInRange( range, self.createElement( 'BR' ) );
|
insertNodeInRange( range, self.createElement( 'BR' ) );
|
||||||
range.collapse( false );
|
range.collapse( false );
|
||||||
self.setSelection( range );
|
self.setSelection( range );
|
||||||
|
@ -4435,6 +4445,12 @@ proto.insertHTML = function ( html, isPaste ) {
|
||||||
this._docWasChanged();
|
this._docWasChanged();
|
||||||
}
|
}
|
||||||
range.collapse( false );
|
range.collapse( false );
|
||||||
|
|
||||||
|
// After inserting the fragment, check whether the cursor is inside
|
||||||
|
// an <a> element and if so if there is an equivalent cursor
|
||||||
|
// position after the <a> element. If there is, move it there.
|
||||||
|
moveRangeBoundaryOutOf( range, 'A', root );
|
||||||
|
|
||||||
this._ensureBottomLine();
|
this._ensureBottomLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1898,6 +1898,12 @@ proto.insertHTML = function ( html, isPaste ) {
|
||||||
this._docWasChanged();
|
this._docWasChanged();
|
||||||
}
|
}
|
||||||
range.collapse( false );
|
range.collapse( false );
|
||||||
|
|
||||||
|
// After inserting the fragment, check whether the cursor is inside
|
||||||
|
// an <a> element and if so if there is an equivalent cursor
|
||||||
|
// position after the <a> element. If there is, move it there.
|
||||||
|
moveRangeBoundaryOutOf( range, 'A', root );
|
||||||
|
|
||||||
this._ensureBottomLine();
|
this._ensureBottomLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,12 +227,7 @@ var handleEnter = function ( self, shiftKey, range ) {
|
||||||
// just play it safe and insert a <br>.
|
// just play it safe and insert a <br>.
|
||||||
if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) {
|
if ( !block || shiftKey || /^T[HD]$/.test( block.nodeName ) ) {
|
||||||
// If inside an <a>, move focus out
|
// If inside an <a>, move focus out
|
||||||
parent = getNearest( range.endContainer, root, 'A' );
|
moveRangeBoundaryOutOf( range, 'A', root );
|
||||||
if ( parent ) {
|
|
||||||
parent = parent.parentNode;
|
|
||||||
moveRangeBoundariesUpTree( range, parent, parent, root );
|
|
||||||
range.collapse( false );
|
|
||||||
}
|
|
||||||
insertNodeInRange( range, self.createElement( 'BR' ) );
|
insertNodeInRange( range, self.createElement( 'BR' ) );
|
||||||
range.collapse( false );
|
range.collapse( false );
|
||||||
self.setSelection( range );
|
self.setSelection( range );
|
||||||
|
@ -587,7 +582,7 @@ const changeIndentationLevel = function ( methodIfInQuote, methodIfInList ) {
|
||||||
return function ( self, event ) {
|
return function ( self, event ) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var path = self.getPath();
|
var path = self.getPath();
|
||||||
if ( /(?:^|>)BLOCKQUOTE/.test( path ) ||
|
if ( /(?:^|>)BLOCKQUOTE/.test( path ) ||
|
||||||
!/(?:^|>)[OU]L/.test( path ) ) {
|
!/(?:^|>)[OU]L/.test( path ) ) {
|
||||||
self[ methodIfInQuote ]();
|
self[ methodIfInQuote ]();
|
||||||
} else {
|
} else {
|
||||||
|
@ -604,9 +599,9 @@ keyHandlers[ ctrlKey + 'shift-5' ] = mapKeyToFormat( 'SUB', { tag: 'SUP' } );
|
||||||
keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } );
|
keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } );
|
||||||
keyHandlers[ ctrlKey + 'shift-8' ] = mapKeyTo( 'makeUnorderedList' );
|
keyHandlers[ ctrlKey + 'shift-8' ] = mapKeyTo( 'makeUnorderedList' );
|
||||||
keyHandlers[ ctrlKey + 'shift-9' ] = mapKeyTo( 'makeOrderedList' );
|
keyHandlers[ ctrlKey + 'shift-9' ] = mapKeyTo( 'makeOrderedList' );
|
||||||
keyHandlers[ ctrlKey + '[' ] =
|
keyHandlers[ ctrlKey + '[' ] =
|
||||||
changeIndentationLevel( 'decreaseQuoteLevel', 'decreaseListLevel' );
|
changeIndentationLevel( 'decreaseQuoteLevel', 'decreaseListLevel' );
|
||||||
keyHandlers[ ctrlKey + ']' ] =
|
keyHandlers[ ctrlKey + ']' ] =
|
||||||
changeIndentationLevel( 'increaseQuoteLevel', 'increaseListLevel' );
|
changeIndentationLevel( 'increaseQuoteLevel', 'increaseListLevel' );
|
||||||
keyHandlers[ ctrlKey + 'd' ] = mapKeyTo( 'toggleCode' );
|
keyHandlers[ ctrlKey + 'd' ] = mapKeyTo( 'toggleCode' );
|
||||||
keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' );
|
keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' );
|
||||||
|
|
|
@ -411,6 +411,9 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( true ) {
|
while ( true ) {
|
||||||
|
if ( endContainer === endMax || endContainer === root ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if ( maySkipBR &&
|
if ( maySkipBR &&
|
||||||
endContainer.nodeType !== TEXT_NODE &&
|
endContainer.nodeType !== TEXT_NODE &&
|
||||||
endContainer.childNodes[ endOffset ] &&
|
endContainer.childNodes[ endOffset ] &&
|
||||||
|
@ -418,9 +421,7 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
||||||
endOffset += 1;
|
endOffset += 1;
|
||||||
maySkipBR = false;
|
maySkipBR = false;
|
||||||
}
|
}
|
||||||
if ( endContainer === endMax ||
|
if ( endOffset !== getLength( endContainer ) ) {
|
||||||
endContainer === root ||
|
|
||||||
endOffset !== getLength( endContainer ) ) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parent = endContainer.parentNode;
|
parent = endContainer.parentNode;
|
||||||
|
@ -432,6 +433,20 @@ var moveRangeBoundariesUpTree = function ( range, startMax, endMax, root ) {
|
||||||
range.setEnd( endContainer, endOffset );
|
range.setEnd( endContainer, endOffset );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var moveRangeBoundaryOutOf = function ( range, nodeName, root ) {
|
||||||
|
var parent = getNearest( range.endContainer, root, 'A' );
|
||||||
|
if ( parent ) {
|
||||||
|
var clone = range.cloneRange();
|
||||||
|
parent = parent.parentNode;
|
||||||
|
moveRangeBoundariesUpTree( clone, parent, parent, root );
|
||||||
|
if ( clone.endContainer === parent ) {
|
||||||
|
range.setStart( clone.endContainer, clone.endOffset );
|
||||||
|
range.setEnd( clone.endContainer, clone.endOffset );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
// Returns the first block at least partially contained by the range,
|
// Returns the first block at least partially contained by the range,
|
||||||
// or null if no block is contained by the range.
|
// or null if no block is contained by the range.
|
||||||
var getStartBlockOfRange = function ( range, root ) {
|
var getStartBlockOfRange = function ( range, root ) {
|
||||||
|
|
Loading…
Reference in a new issue