diff --git a/build/squire-raw.js b/build/squire-raw.js index e465bc9..47d8db8 100644 --- a/build/squire-raw.js +++ b/build/squire-raw.js @@ -3620,6 +3620,88 @@ proto.setTextDirection = function ( direction ) { return this.focus(); }; + +function forEachChildInRange( rootNode, range, iterator ) { + var walker = new TreeWalker( rootNode, SHOW_ELEMENT, + function ( node ) { + return node.parentNode === rootNode && + isNodeContainedInRange( range, node, false /* include partials */ ); + } + ); + var node; + while ( node = walker.nextNode() ) { + iterator( node ); + } +} + +function mapEachChildInRange( rootNode, range, iterator ) { + var output = []; + forEachChildInRange( rootNode, range, function ( node ) { + output.push( iterator( node ) ); + } ); + return output; +} + +var stylingNodeNames = /(^|>)(?:B|I|S|SUB|SUP|U|BLOCKQUOTE|OL|UL|LI|T(?:ABLE|BODY|HEAD|FOOT|R|D|H))(>|$)/; + +proto.removeAllFormatting = function ( range ) { + if ( !range && !( range = this.getSelection() ) || range.collapsed ) { + return false; + } + + var stopNode = range.commonAncestorContainer; + while ( stylingNodeNames.test( getPath( stopNode ) ) ) { + stopNode = stopNode.parentNode; + } + if (stopNode.nodeType === TEXT_NODE) { + return false; + } + + moveRangeBoundariesUpTree( range, stopNode ); + this._saveRangeToBookmark( range ); + + var doc = stopNode.ownerDocument; + + var startContainer = range.startContainer; + var startOffset = range.startOffset; + var endContainer = range.endContainer; + var endOffset = range.endOffset; + // Split end point first to avoid problems when end and start in same container. + split( endContainer, endOffset, stopNode ); + split( startContainer, startOffset, stopNode ); + + range = this._getRangeAndRemoveBookmark(null, true); + moveRangeBoundariesUpTree( range, stopNode ); + this._saveRangeToBookmark( range ); + + var that = this; + + var contents = []; + forEachChildInRange( stopNode, range, function cleanSingleNode( node ) { + if ( isContainer( node ) ) { + forEachChildInRange( node, range, cleanSingleNode ); + } else if ( isBlock( node ) ) { + var block = that.createDefaultBlock(); + block.appendChild( doc.createTextNode( node.textContent ) ); + contents.push( block ); + } else if ( isInline( node ) ) { + contents.push( doc.createTextNode( node.textContent ) ); + } + } ); + var oldContents = mapEachChildInRange( stopNode, range, function ( node ) { + return node; + } ); + + contents.forEach( function ( node ) { + stopNode.insertBefore( node, oldContents[0] ); + } ); + oldContents.forEach( function ( node ) { + stopNode.removeChild( node ); + } ); + + this.setSelection( this._getRangeAndRemoveBookmark() ); +}; + proto.increaseQuoteLevel = command( 'modifyBlocks', increaseBlockQuoteLevel ); proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel ); diff --git a/source/Editor.js b/source/Editor.js index 310e685..5a43aab 100644 --- a/source/Editor.js +++ b/source/Editor.js @@ -2162,6 +2162,88 @@ proto.setTextDirection = function ( direction ) { return this.focus(); }; + +function forEachChildInRange( rootNode, range, iterator ) { + var walker = new TreeWalker( rootNode, SHOW_ELEMENT, + function ( node ) { + return node.parentNode === rootNode && + isNodeContainedInRange( range, node, false /* include partials */ ); + } + ); + var node; + while ( node = walker.nextNode() ) { + iterator( node ); + } +} + +function mapEachChildInRange( rootNode, range, iterator ) { + var output = []; + forEachChildInRange( rootNode, range, function ( node ) { + output.push( iterator( node ) ); + } ); + return output; +} + +var stylingNodeNames = /(^|>)(?:B|I|S|SUB|SUP|U|BLOCKQUOTE|OL|UL|LI|T(?:ABLE|BODY|HEAD|FOOT|R|D|H))(>|$)/; + +proto.removeAllFormatting = function ( range ) { + if ( !range && !( range = this.getSelection() ) || range.collapsed ) { + return false; + } + + var stopNode = range.commonAncestorContainer; + while ( stylingNodeNames.test( getPath( stopNode ) ) ) { + stopNode = stopNode.parentNode; + } + if (stopNode.nodeType === TEXT_NODE) { + return false; + } + + moveRangeBoundariesUpTree( range, stopNode ); + this._saveRangeToBookmark( range ); + + var doc = stopNode.ownerDocument; + + var startContainer = range.startContainer; + var startOffset = range.startOffset; + var endContainer = range.endContainer; + var endOffset = range.endOffset; + // Split end point first to avoid problems when end and start in same container. + split( endContainer, endOffset, stopNode ); + split( startContainer, startOffset, stopNode ); + + range = this._getRangeAndRemoveBookmark(null, true); + moveRangeBoundariesUpTree( range, stopNode ); + this._saveRangeToBookmark( range ); + + var that = this; + + var contents = []; + forEachChildInRange( stopNode, range, function cleanSingleNode( node ) { + if ( isContainer( node ) ) { + forEachChildInRange( node, range, cleanSingleNode ); + } else if ( isBlock( node ) ) { + var block = that.createDefaultBlock(); + block.appendChild( doc.createTextNode( node.textContent ) ); + contents.push( block ); + } else if ( isInline( node ) ) { + contents.push( doc.createTextNode( node.textContent ) ); + } + } ); + var oldContents = mapEachChildInRange( stopNode, range, function ( node ) { + return node; + } ); + + contents.forEach( function ( node ) { + stopNode.insertBefore( node, oldContents[0] ); + } ); + oldContents.forEach( function ( node ) { + stopNode.removeChild( node ); + } ); + + this.setSelection( this._getRangeAndRemoveBookmark() ); +}; + proto.increaseQuoteLevel = command( 'modifyBlocks', increaseBlockQuoteLevel ); proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel );