mirror of
https://github.com/fastmail/Squire.git
synced 2025-01-03 13:16:31 -05:00
If selection is empty, expand to current line when removing PREs.
This commit is contained in:
parent
6ba03f6989
commit
8a8285560e
4 changed files with 210 additions and 108 deletions
|
@ -3551,9 +3551,9 @@ var getTextFromHTMLFragment = function ( self, frag ) {
|
|||
var makePreformatted = function ( frag ) {
|
||||
return this.createElement( 'PRE',
|
||||
this._config.tagAttributes.pre, [
|
||||
this.createElement( 'DIV', { id: startSelectionId } ),
|
||||
this.createElement( 'INPUT', { id: startSelectionId, type: 'hidden' } ),
|
||||
getTextFromHTMLFragment( this, frag ),
|
||||
this.createElement( 'DIV', { id: endSelectionId } )
|
||||
this.createElement( 'INPUT', { id: endSelectionId, type: 'hidden' } )
|
||||
] );
|
||||
};
|
||||
|
||||
|
@ -3561,64 +3561,98 @@ var removePreformatted = function ( frag ) {
|
|||
var range = this._doc.createRange();
|
||||
var startRangeMarker = frag.querySelector( '#' + startSelectionId );
|
||||
var endRangeMarker = frag.querySelector( '#' + endSelectionId );
|
||||
if (!startRangeMarker || !endRangeMarker) { return frag; }
|
||||
if ( !startRangeMarker || !endRangeMarker ) {
|
||||
return frag;
|
||||
}
|
||||
|
||||
range.setStartBefore( startRangeMarker );
|
||||
range.setEndAfter( endRangeMarker );
|
||||
|
||||
if (!range || range.collapsed) {
|
||||
return frag; // Maybe remove whole element instead? Single line?
|
||||
var preElems = frag.querySelectorAll('pre');
|
||||
if ( preElems.length === 0 ) {
|
||||
return frag;
|
||||
} else {
|
||||
var preElems = frag.querySelectorAll('pre');
|
||||
if ( preElems.length === 0 ) {
|
||||
return frag;
|
||||
} else {
|
||||
var firstPre = preElems[0],
|
||||
lastPre = preElems[preElems.length - 1],
|
||||
startContainer = range.startContainer,
|
||||
startOffset = range.startOffset,
|
||||
endContainer = range.endContainer,
|
||||
endOffset = range.endOffset,
|
||||
splitElems = [];
|
||||
if ( getNearest( endContainer, 'PRE' ) === lastPre &&
|
||||
endOffset < endContainer.childNodes.length - 1 ) {
|
||||
splitElems.push( split( endContainer, endOffset, lastPre.parentNode ) );
|
||||
}
|
||||
if ( getNearest( startContainer, 'PRE' ) === firstPre &&
|
||||
startOffset > 0 ) {
|
||||
split( startContainer, startOffset, firstPre.parentNode );
|
||||
splitElems.push( startContainer );
|
||||
}
|
||||
preElems = frag.querySelectorAll('pre');
|
||||
range.setStartBefore( startRangeMarker );
|
||||
range.setEndAfter( endRangeMarker );
|
||||
var self = this,
|
||||
node, elemsInRange;
|
||||
for ( var i = 0; i < preElems.length; i += 1 ) {
|
||||
node = preElems[i];
|
||||
if ( isNodeContainedInRange( range, node, true ) ) {
|
||||
var replacement = this._doc.createDocumentFragment(),
|
||||
childNode;
|
||||
while ( childNode = node.childNodes[0] ) {
|
||||
if ( childNode.nodeType === TEXT_NODE ) {
|
||||
// replace all text nodes with HTMLified version of text content (1 DIV per line)
|
||||
var nodeLines = childNode.nodeValue.split( '\n' );
|
||||
/*jshint loopfunc: true*/
|
||||
nodeLines.forEach( function (line) {
|
||||
var div = self.createDefaultBlock( [ self._doc.createTextNode ( line ) ] );
|
||||
replacement.appendChild( div );
|
||||
});
|
||||
/*jshint loopfunc: false*/
|
||||
node.removeChild( childNode );
|
||||
} else {
|
||||
replacement.appendChild( childNode );
|
||||
}
|
||||
}
|
||||
replaceWith( node, replacement );
|
||||
var firstPre = preElems[0],
|
||||
lastPre = preElems[preElems.length - 1],
|
||||
startContainer = range.startContainer,
|
||||
startOffset = range.startOffset,
|
||||
endContainer = range.endContainer,
|
||||
endOffset = range.endOffset,
|
||||
splitElems = [];
|
||||
if ( startRangeMarker.nextSibling === endRangeMarker ) {
|
||||
// Collapsed selection - expand to the whole line
|
||||
var beforeStart = startRangeMarker.previousSibling,
|
||||
newNode;
|
||||
if ( beforeStart && beforeStart.nodeType === TEXT_NODE ) {
|
||||
var lineStartOffset = beforeStart.nodeValue.lastIndexOf( '\n' );
|
||||
if ( lineStartOffset !== -1 ) {
|
||||
newNode = beforeStart.splitText( lineStartOffset );
|
||||
beforeStart.parentNode.insertBefore( startRangeMarker, newNode );
|
||||
} else {
|
||||
beforeStart.parentNode.insertBefore( startRangeMarker, beforeStart );
|
||||
}
|
||||
range.setStartBefore( startRangeMarker );
|
||||
startOffset = range.startOffset;
|
||||
startContainer = range.startContainer;
|
||||
}
|
||||
return frag;
|
||||
var afterEnd = endRangeMarker.nextSibling;
|
||||
if ( afterEnd && afterEnd.nodeType === TEXT_NODE ) {
|
||||
var lineEndOffset = afterEnd.nodeValue.indexOf( '\n' );
|
||||
if ( lineEndOffset !== -1 ) {
|
||||
newNode = afterEnd.splitText( lineEndOffset + 1 );
|
||||
afterEnd.parentNode.insertBefore( endRangeMarker, newNode );
|
||||
} else {
|
||||
if ( afterEnd.nextSibling ) {
|
||||
afterEnd.parentNode.insertBefore( endRangeMarker, afterEnd.nextSibling );
|
||||
} else {
|
||||
afterEnd.parentNode.appendChild( endRangeMarker );
|
||||
}
|
||||
}
|
||||
range.setEndBefore( endRangeMarker );
|
||||
endOffset = range.endOffset;
|
||||
endContainer = range.endContainer;
|
||||
}
|
||||
startContainer.normalize();
|
||||
}
|
||||
if ( getNearest( endContainer, 'PRE' ) === lastPre &&
|
||||
endOffset < endContainer.childNodes.length - 1 ) {
|
||||
splitElems.push( split( endContainer, endOffset, lastPre.parentNode ) );
|
||||
}
|
||||
if ( getNearest( startContainer, 'PRE' ) === firstPre &&
|
||||
startOffset > 0 ) {
|
||||
split( startContainer, startOffset, firstPre.parentNode );
|
||||
splitElems.push( startContainer );
|
||||
}
|
||||
preElems = frag.querySelectorAll('pre');
|
||||
range.setStartBefore( startRangeMarker );
|
||||
range.setEndAfter( endRangeMarker );
|
||||
var self = this,
|
||||
node, elemsInRange;
|
||||
for ( var i = 0; i < preElems.length; i += 1 ) {
|
||||
node = preElems[i];
|
||||
if ( isNodeContainedInRange( range, node, true ) ) {
|
||||
var replacement = this._doc.createDocumentFragment(),
|
||||
childNode;
|
||||
while ( childNode = node.childNodes[0] ) {
|
||||
if ( childNode.nodeType === TEXT_NODE ) {
|
||||
// replace all text nodes with HTMLified version of text content (1 DIV per line)
|
||||
var nodeLines = childNode.nodeValue.split( '\n' );
|
||||
/*jshint loopfunc: true*/
|
||||
nodeLines.forEach( function (line) {
|
||||
if ( !line ) { return; }
|
||||
var div = self.createDefaultBlock( [ self._doc.createTextNode ( line ) ] );
|
||||
replacement.appendChild( div );
|
||||
});
|
||||
/*jshint loopfunc: false*/
|
||||
node.removeChild( childNode );
|
||||
} else {
|
||||
replacement.appendChild( childNode );
|
||||
}
|
||||
}
|
||||
replaceWith( node, replacement );
|
||||
}
|
||||
}
|
||||
return frag;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
138
source/Editor.js
138
source/Editor.js
|
@ -1352,9 +1352,9 @@ var getTextFromHTMLFragment = function ( self, frag ) {
|
|||
var makePreformatted = function ( frag ) {
|
||||
return this.createElement( 'PRE',
|
||||
this._config.tagAttributes.pre, [
|
||||
this.createElement( 'DIV', { id: startSelectionId } ),
|
||||
this.createElement( 'INPUT', { id: startSelectionId, type: 'hidden' } ),
|
||||
getTextFromHTMLFragment( this, frag ),
|
||||
this.createElement( 'DIV', { id: endSelectionId } )
|
||||
this.createElement( 'INPUT', { id: endSelectionId, type: 'hidden' } )
|
||||
] );
|
||||
};
|
||||
|
||||
|
@ -1362,64 +1362,98 @@ var removePreformatted = function ( frag ) {
|
|||
var range = this._doc.createRange();
|
||||
var startRangeMarker = frag.querySelector( '#' + startSelectionId );
|
||||
var endRangeMarker = frag.querySelector( '#' + endSelectionId );
|
||||
if (!startRangeMarker || !endRangeMarker) { return frag; }
|
||||
if ( !startRangeMarker || !endRangeMarker ) {
|
||||
return frag;
|
||||
}
|
||||
|
||||
range.setStartBefore( startRangeMarker );
|
||||
range.setEndAfter( endRangeMarker );
|
||||
|
||||
if (!range || range.collapsed) {
|
||||
return frag; // Maybe remove whole element instead? Single line?
|
||||
var preElems = frag.querySelectorAll('pre');
|
||||
if ( preElems.length === 0 ) {
|
||||
return frag;
|
||||
} else {
|
||||
var preElems = frag.querySelectorAll('pre');
|
||||
if ( preElems.length === 0 ) {
|
||||
return frag;
|
||||
} else {
|
||||
var firstPre = preElems[0],
|
||||
lastPre = preElems[preElems.length - 1],
|
||||
startContainer = range.startContainer,
|
||||
startOffset = range.startOffset,
|
||||
endContainer = range.endContainer,
|
||||
endOffset = range.endOffset,
|
||||
splitElems = [];
|
||||
if ( getNearest( endContainer, 'PRE' ) === lastPre &&
|
||||
endOffset < endContainer.childNodes.length - 1 ) {
|
||||
splitElems.push( split( endContainer, endOffset, lastPre.parentNode ) );
|
||||
}
|
||||
if ( getNearest( startContainer, 'PRE' ) === firstPre &&
|
||||
startOffset > 0 ) {
|
||||
split( startContainer, startOffset, firstPre.parentNode );
|
||||
splitElems.push( startContainer );
|
||||
}
|
||||
preElems = frag.querySelectorAll('pre');
|
||||
range.setStartBefore( startRangeMarker );
|
||||
range.setEndAfter( endRangeMarker );
|
||||
var self = this,
|
||||
node, elemsInRange;
|
||||
for ( var i = 0; i < preElems.length; i += 1 ) {
|
||||
node = preElems[i];
|
||||
if ( isNodeContainedInRange( range, node, true ) ) {
|
||||
var replacement = this._doc.createDocumentFragment(),
|
||||
childNode;
|
||||
while ( childNode = node.childNodes[0] ) {
|
||||
if ( childNode.nodeType === TEXT_NODE ) {
|
||||
// replace all text nodes with HTMLified version of text content (1 DIV per line)
|
||||
var nodeLines = childNode.nodeValue.split( '\n' );
|
||||
/*jshint loopfunc: true*/
|
||||
nodeLines.forEach( function (line) {
|
||||
var div = self.createDefaultBlock( [ self._doc.createTextNode ( line ) ] );
|
||||
replacement.appendChild( div );
|
||||
});
|
||||
/*jshint loopfunc: false*/
|
||||
node.removeChild( childNode );
|
||||
} else {
|
||||
replacement.appendChild( childNode );
|
||||
}
|
||||
}
|
||||
replaceWith( node, replacement );
|
||||
var firstPre = preElems[0],
|
||||
lastPre = preElems[preElems.length - 1],
|
||||
startContainer = range.startContainer,
|
||||
startOffset = range.startOffset,
|
||||
endContainer = range.endContainer,
|
||||
endOffset = range.endOffset,
|
||||
splitElems = [];
|
||||
if ( startRangeMarker.nextSibling === endRangeMarker ) {
|
||||
// Collapsed selection - expand to the whole line
|
||||
var beforeStart = startRangeMarker.previousSibling,
|
||||
newNode;
|
||||
if ( beforeStart && beforeStart.nodeType === TEXT_NODE ) {
|
||||
var lineStartOffset = beforeStart.nodeValue.lastIndexOf( '\n' );
|
||||
if ( lineStartOffset !== -1 ) {
|
||||
newNode = beforeStart.splitText( lineStartOffset );
|
||||
beforeStart.parentNode.insertBefore( startRangeMarker, newNode );
|
||||
} else {
|
||||
beforeStart.parentNode.insertBefore( startRangeMarker, beforeStart );
|
||||
}
|
||||
range.setStartBefore( startRangeMarker );
|
||||
startOffset = range.startOffset;
|
||||
startContainer = range.startContainer;
|
||||
}
|
||||
return frag;
|
||||
var afterEnd = endRangeMarker.nextSibling;
|
||||
if ( afterEnd && afterEnd.nodeType === TEXT_NODE ) {
|
||||
var lineEndOffset = afterEnd.nodeValue.indexOf( '\n' );
|
||||
if ( lineEndOffset !== -1 ) {
|
||||
newNode = afterEnd.splitText( lineEndOffset + 1 );
|
||||
afterEnd.parentNode.insertBefore( endRangeMarker, newNode );
|
||||
} else {
|
||||
if ( afterEnd.nextSibling ) {
|
||||
afterEnd.parentNode.insertBefore( endRangeMarker, afterEnd.nextSibling );
|
||||
} else {
|
||||
afterEnd.parentNode.appendChild( endRangeMarker );
|
||||
}
|
||||
}
|
||||
range.setEndBefore( endRangeMarker );
|
||||
endOffset = range.endOffset;
|
||||
endContainer = range.endContainer;
|
||||
}
|
||||
startContainer.normalize();
|
||||
}
|
||||
if ( getNearest( endContainer, 'PRE' ) === lastPre &&
|
||||
endOffset < endContainer.childNodes.length - 1 ) {
|
||||
splitElems.push( split( endContainer, endOffset, lastPre.parentNode ) );
|
||||
}
|
||||
if ( getNearest( startContainer, 'PRE' ) === firstPre &&
|
||||
startOffset > 0 ) {
|
||||
split( startContainer, startOffset, firstPre.parentNode );
|
||||
splitElems.push( startContainer );
|
||||
}
|
||||
preElems = frag.querySelectorAll('pre');
|
||||
range.setStartBefore( startRangeMarker );
|
||||
range.setEndAfter( endRangeMarker );
|
||||
var self = this,
|
||||
node, elemsInRange;
|
||||
for ( var i = 0; i < preElems.length; i += 1 ) {
|
||||
node = preElems[i];
|
||||
if ( isNodeContainedInRange( range, node, true ) ) {
|
||||
var replacement = this._doc.createDocumentFragment(),
|
||||
childNode;
|
||||
while ( childNode = node.childNodes[0] ) {
|
||||
if ( childNode.nodeType === TEXT_NODE ) {
|
||||
// replace all text nodes with HTMLified version of text content (1 DIV per line)
|
||||
var nodeLines = childNode.nodeValue.split( '\n' );
|
||||
/*jshint loopfunc: true*/
|
||||
nodeLines.forEach( function (line) {
|
||||
if ( !line ) { return; }
|
||||
var div = self.createDefaultBlock( [ self._doc.createTextNode ( line ) ] );
|
||||
replacement.appendChild( div );
|
||||
});
|
||||
/*jshint loopfunc: false*/
|
||||
node.removeChild( childNode );
|
||||
} else {
|
||||
replacement.appendChild( childNode );
|
||||
}
|
||||
}
|
||||
replaceWith( node, replacement );
|
||||
}
|
||||
}
|
||||
return frag;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ describe('Squire RTE', function () {
|
|||
expect(editor, 'to contain HTML', startHTML);
|
||||
selectAll(editor);
|
||||
editor.removePreformatted();
|
||||
expect(editor, 'to contain HTML', '<div>abc</div><div></div><div>one two three four five</div><div></div>');
|
||||
expect(editor, 'to contain HTML', '<div>abc</div><div>one two three four five</div>');
|
||||
});
|
||||
|
||||
it('cuts the beginning off PRE tags', function () {
|
||||
|
@ -254,7 +254,7 @@ describe('Squire RTE', function () {
|
|||
range.setEnd(doc.querySelector('pre').childNodes[0], 18);
|
||||
editor.setSelection(range);
|
||||
editor.removePreformatted();
|
||||
expect(editor, 'to contain HTML', '<div>abc</div><div></div><div>one two three</div><pre> four five\n</pre>');
|
||||
expect(editor, 'to contain HTML', '<div>abc</div><div>one two three</div><pre> four five\n</pre>');
|
||||
});
|
||||
|
||||
it('cuts the end off PRE tags', function () {
|
||||
|
@ -268,6 +268,40 @@ describe('Squire RTE', function () {
|
|||
editor.removePreformatted();
|
||||
expect(editor, 'to contain HTML', '<pre>abc\n\none two three</pre><div> four five</div>');
|
||||
});
|
||||
|
||||
describe('with collapsed selection', function () {
|
||||
beforeEach(function () {
|
||||
var startHTML = '<pre>abc\n\none two three four five\nxyz</pre>';
|
||||
editor.setHTML(startHTML);
|
||||
});
|
||||
|
||||
it('expands selection to the whole line', function () {
|
||||
var range = doc.createRange();
|
||||
range.setStart(doc.querySelector('pre').childNodes[0], 24);
|
||||
range.setEnd(doc.querySelector('pre').childNodes[0], 24);
|
||||
editor.setSelection(range);
|
||||
editor.removePreformatted();
|
||||
expect(editor, 'to contain HTML', '<pre>abc\n</pre><div>one two three four five</div><pre>xyz</pre>');
|
||||
});
|
||||
|
||||
it('... even when on first line of tag', function () {
|
||||
var range = doc.createRange();
|
||||
range.setStart(doc.querySelector('pre').childNodes[0], 2);
|
||||
range.setEnd(doc.querySelector('pre').childNodes[0], 2);
|
||||
editor.setSelection(range);
|
||||
editor.removePreformatted();
|
||||
expect(editor, 'to contain HTML', '<div>abc</div><pre>\none two three four five\nxyz</pre>');
|
||||
});
|
||||
|
||||
it('... or on last line of tag', function () {
|
||||
var range = doc.createRange();
|
||||
range.setStart(doc.querySelector('pre').childNodes[0], 31);
|
||||
range.setEnd(doc.querySelector('pre').childNodes[0], 31);
|
||||
editor.setSelection(range);
|
||||
editor.removePreformatted();
|
||||
expect(editor, 'to contain HTML', '<pre>abc\n\none two three four five</pre><div>xyz</div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
|
|
Loading…
Reference in a new issue