0
Fork 0
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:
Gert K. Sønderby 2015-11-25 14:41:06 +01:00 committed by Gert Sønderby
parent 6ba03f6989
commit 8a8285560e
4 changed files with 210 additions and 108 deletions

View file

@ -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

View file

@ -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;
}
};

View file

@ -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 () {