0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2025-01-03 05:00:13 -05:00

Turn Range prototype extensions into functions.

A step towards being able to share code between multiple concurrent instances of
the editor. Also reduces minified size slightly.
This commit is contained in:
Neil Jenkins 2013-06-20 21:03:01 +10:00
parent dbda1fa4ed
commit 7812b8db23
4 changed files with 263 additions and 259 deletions

View file

@ -580,9 +580,7 @@ if ( function () {
fixCursor, fixCursor,
split, split,
mergeWithBlock, mergeWithBlock,
mergeContainers, mergeContainers
Range
*/ */
/*jshint strict:false */ /*jshint strict:false */
@ -611,11 +609,11 @@ var getNodeAfter = function ( node, offset ) {
return node; return node;
}; };
var RangePrototype = Range.prototype; // ---
RangePrototype.forEachTextNode = function ( fn ) { var forEachTextNodeInRange = function ( range, fn ) {
var range = this.cloneRange(); range = range.cloneRange();
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
var startContainer = range.startContainer, var startContainer = range.startContainer,
endContainer = range.endContainer, endContainer = range.endContainer,
@ -631,9 +629,9 @@ RangePrototype.forEachTextNode = function ( fn ) {
( textnode = walker.nextNode() ) ) {} ( textnode = walker.nextNode() ) ) {}
}; };
RangePrototype.getTextContent = function () { var getTextContentInRange = function ( range ) {
var textContent = ''; var textContent = '';
this.forEachTextNode( function ( textnode, range ) { forEachTextNodeInRange( range, function ( textnode, range ) {
var value = textnode.data; var value = textnode.data;
if ( value && ( /\S/.test( value ) ) ) { if ( value && ( /\S/.test( value ) ) ) {
if ( textnode === range.endContainer ) { if ( textnode === range.endContainer ) {
@ -650,12 +648,12 @@ RangePrototype.getTextContent = function () {
// --- // ---
RangePrototype._insertNode = function ( node ) { var insertNodeInRange = function ( range, node ) {
// Insert at start. // Insert at start.
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
parent, children, childCount, afterSplit; parent, children, childCount, afterSplit;
// If part way through a text node, split it. // If part way through a text node, split it.
@ -664,7 +662,7 @@ RangePrototype._insertNode = function ( node ) {
children = parent.childNodes; children = parent.childNodes;
if ( startOffset === startContainer.length ) { if ( startOffset === startContainer.length ) {
startOffset = indexOf.call( children, startContainer ) + 1; startOffset = indexOf.call( children, startContainer ) + 1;
if ( this.collapsed ) { if ( range.collapsed ) {
endContainer = parent; endContainer = parent;
endOffset = startOffset; endOffset = startOffset;
} }
@ -698,20 +696,18 @@ RangePrototype._insertNode = function ( node ) {
endOffset += children.length - childCount; endOffset += children.length - childCount;
} }
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
return this;
}; };
RangePrototype._extractContents = function ( common ) { var extractContentsOfRange = function ( range, common ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset; endOffset = range.endOffset;
if ( !common ) { if ( !common ) {
common = this.commonAncestorContainer; common = range.commonAncestorContainer;
} }
if ( common.nodeType === TEXT_NODE ) { if ( common.nodeType === TEXT_NODE ) {
@ -730,28 +726,28 @@ RangePrototype._extractContents = function ( common ) {
startNode = next; startNode = next;
} }
this.setStart( common, endNode ? range.setStart( common, endNode ?
indexOf.call( common.childNodes, endNode ) : indexOf.call( common.childNodes, endNode ) :
common.childNodes.length ); common.childNodes.length );
this.collapse( true ); range.collapse( true );
fixCursor( common ); fixCursor( common );
return frag; return frag;
}; };
RangePrototype._deleteContents = function () { var deleteContentsOfRange = function ( range ) {
// Move boundaries up as much as possible to reduce need to split. // Move boundaries up as much as possible to reduce need to split.
this.moveBoundariesUpTree(); moveRangeBoundariesUpTree( range );
// Remove selected range // Remove selected range
this._extractContents(); extractContentsOfRange( range );
// If we split into two different blocks, merge the blocks. // If we split into two different blocks, merge the blocks.
var startBlock = this.getStartBlock(), var startBlock = getStartBlockOfRange( range ),
endBlock = this.getEndBlock(); endBlock = getEndBlockOfRange( range );
if ( startBlock && endBlock && startBlock !== endBlock ) { if ( startBlock && endBlock && startBlock !== endBlock ) {
mergeWithBlock( startBlock, endBlock, this ); mergeWithBlock( startBlock, endBlock, range );
} }
// Ensure block has necessary children // Ensure block has necessary children
@ -760,27 +756,25 @@ RangePrototype._deleteContents = function () {
} }
// Ensure body has a block-level element in it. // Ensure body has a block-level element in it.
var body = this.endContainer.ownerDocument.body, var body = range.endContainer.ownerDocument.body,
child = body.firstChild; child = body.firstChild;
if ( !child || child.nodeName === 'BR' ) { if ( !child || child.nodeName === 'BR' ) {
fixCursor( body ); fixCursor( body );
this.selectNodeContents( body.firstChild ); range.selectNodeContents( body.firstChild );
} }
// Ensure valid range (must have only block or inline containers) // Ensure valid range (must have only block or inline containers)
var isCollapsed = this.collapsed; var isCollapsed = range.collapsed;
this.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
if ( isCollapsed ) { if ( isCollapsed ) {
// Collapse // Collapse
this.collapse( true ); range.collapse( true );
} }
return this;
}; };
// --- // ---
RangePrototype.insertTreeFragment = function ( frag ) { var insertTreeFragmentIntoRange = function ( range, frag ) {
// Check if it's all inline content // Check if it's all inline content
var allInline = true, var allInline = true,
children = frag.childNodes, children = frag.childNodes,
@ -793,23 +787,23 @@ RangePrototype.insertTreeFragment = function ( frag ) {
} }
// Delete any selected content // Delete any selected content
if ( !this.collapsed ) { if ( !range.collapsed ) {
this._deleteContents(); deleteContentsOfRange( range );
} }
// Move range down into text ndoes // Move range down into text ndoes
this.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
// If inline, just insert at the current position. // If inline, just insert at the current position.
if ( allInline ) { if ( allInline ) {
this._insertNode( frag ); insertNodeInRange( range, frag );
this.collapse( false ); range.collapse( false );
} }
// Otherwise, split up to body, insert inline before and after split // Otherwise, split up to body, insert inline before and after split
// and insert block in between split, then merge containers. // and insert block in between split, then merge containers.
else { else {
var nodeAfterSplit = split( this.startContainer, this.startOffset, var nodeAfterSplit = split( range.startContainer, range.startOffset,
this.startContainer.ownerDocument.body ), range.startContainer.ownerDocument.body ),
nodeBeforeSplit = nodeAfterSplit.previousSibling, nodeBeforeSplit = nodeAfterSplit.previousSibling,
startContainer = nodeBeforeSplit, startContainer = nodeBeforeSplit,
startOffset = startContainer.childNodes.length, startOffset = startContainer.childNodes.length,
@ -865,17 +859,16 @@ RangePrototype.insertTreeFragment = function ( frag ) {
mergeContainers( nodeBeforeSplit ); mergeContainers( nodeBeforeSplit );
} }
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
this.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
} }
}; };
// --- // ---
RangePrototype.containsNode = function ( node, partial ) { var isNodeContainedInRange = function ( range, node, partial ) {
var range = this, var nodeRange = node.ownerDocument.createRange();
nodeRange = node.ownerDocument.createRange();
nodeRange.selectNode( node ); nodeRange.selectNode( node );
@ -899,11 +892,11 @@ RangePrototype.containsNode = function ( node, partial ) {
} }
}; };
RangePrototype.moveBoundariesDownTree = function () { var moveRangeBoundariesDownTree = function ( range ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
child; child;
while ( startContainer.nodeType !== TEXT_NODE ) { while ( startContainer.nodeType !== TEXT_NODE ) {
@ -936,26 +929,24 @@ RangePrototype.moveBoundariesDownTree = function () {
// If collapsed, this algorithm finds the nearest text node positions // If collapsed, this algorithm finds the nearest text node positions
// *outside* the range rather than inside, but also it flips which is // *outside* the range rather than inside, but also it flips which is
// assigned to which. // assigned to which.
if ( this.collapsed ) { if ( range.collapsed ) {
this.setStart( endContainer, endOffset ); range.setStart( endContainer, endOffset );
this.setEnd( startContainer, startOffset ); range.setEnd( startContainer, startOffset );
} else { } else {
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
} }
return this;
}; };
RangePrototype.moveBoundariesUpTree = function ( common ) { var moveRangeBoundariesUpTree = function ( range, common ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
parent; parent;
if ( !common ) { if ( !common ) {
common = this.commonAncestorContainer; common = range.commonAncestorContainer;
} }
while ( startContainer !== common && !startOffset ) { while ( startContainer !== common && !startOffset ) {
@ -971,16 +962,14 @@ RangePrototype.moveBoundariesUpTree = function ( common ) {
endContainer = parent; endContainer = parent;
} }
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
return this;
}; };
// 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.
RangePrototype.getStartBlock = function () { var getStartBlockOfRange = function ( range ) {
var container = this.startContainer, var container = range.startContainer,
block; block;
// If inline, get the containing block. // If inline, get the containing block.
@ -989,17 +978,17 @@ RangePrototype.getStartBlock = function () {
} else if ( isBlock( container ) ) { } else if ( isBlock( container ) ) {
block = container; block = container;
} else { } else {
block = getNodeBefore( container, this.startOffset ); block = getNodeBefore( container, range.startOffset );
block = getNextBlock( block ); block = getNextBlock( block );
} }
// Check the block actually intersects the range // Check the block actually intersects the range
return block && this.containsNode( block, true ) ? block : null; return block && isNodeContainedInRange( range, block, true ) ? block : null;
}; };
// Returns the last block at least partially contained by the range, // Returns the last 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.
RangePrototype.getEndBlock = function () { var getEndBlockOfRange = function ( range ) {
var container = this.endContainer, var container = range.endContainer,
block, child; block, child;
// If inline, get the containing block. // If inline, get the containing block.
@ -1008,7 +997,7 @@ RangePrototype.getEndBlock = function () {
} else if ( isBlock( container ) ) { } else if ( isBlock( container ) ) {
block = container; block = container;
} else { } else {
block = getNodeAfter( container, this.endOffset ); block = getNodeAfter( container, range.endOffset );
if ( !block ) { if ( !block ) {
block = container.ownerDocument.body; block = container.ownerDocument.body;
while ( child = block.lastChild ) { while ( child = block.lastChild ) {
@ -1019,12 +1008,12 @@ RangePrototype.getEndBlock = function () {
} }
// Check the block actually intersects the range // Check the block actually intersects the range
return block && this.containsNode( block, true ) ? block : null; return block && isNodeContainedInRange( range, block, true ) ? block : null;
}; };
RangePrototype.startsAtBlockBoundary = function () { var rangeDoesStartAtBlockBoundary = function ( range ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
parent, child; parent, child;
while ( isInline( startContainer ) ) { while ( isInline( startContainer ) ) {
@ -1044,9 +1033,9 @@ RangePrototype.startsAtBlockBoundary = function () {
return !startOffset; return !startOffset;
}; };
RangePrototype.endsAtBlockBoundary = function () { var rangeDoesEndAtBlockBoundary = function ( range ) {
var endContainer = this.endContainer, var endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
length = getLength( endContainer ), length = getLength( endContainer ),
parent, child; parent, child;
@ -1068,19 +1057,17 @@ RangePrototype.endsAtBlockBoundary = function () {
return endOffset === length; return endOffset === length;
}; };
RangePrototype.expandToBlockBoundaries = function () { var expandRangeToBlockBoundaries = function ( range ) {
var start = this.getStartBlock(), var start = getStartBlockOfRange( range ),
end = this.getEndBlock(), end = getEndBlockOfRange( range ),
parent; parent;
if ( start && end ) { if ( start && end ) {
parent = start.parentNode; parent = start.parentNode;
this.setStart( parent, indexOf.call( parent.childNodes, start ) ); range.setStart( parent, indexOf.call( parent.childNodes, start ) );
parent = end.parentNode; parent = end.parentNode;
this.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 ); range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 );
} }
return this;
}; };
/*global /*global
DOCUMENT_POSITION_PRECEDING, DOCUMENT_POSITION_PRECEDING,
@ -1128,6 +1115,21 @@ RangePrototype.expandToBlockBoundaries = function () {
mergeContainers, mergeContainers,
createElement, createElement,
forEachTextNodeInRange,
getTextContentInRange,
insertNodeInRange,
extractContentsOfRange,
deleteContentsOfRange,
insertTreeFragmentIntoRange,
isNodeContainedInRange,
moveRangeBoundariesDownTree,
moveRangeBoundariesUpTree,
getStartBlockOfRange,
getEndBlockOfRange,
rangeDoesStartAtBlockBoundary,
rangeDoesEndAtBlockBoundary,
expandRangeToBlockBoundaries,
Range, Range,
top, top,
console, console,
@ -1405,11 +1407,11 @@ var insertElement = function ( el, range ) {
if ( !range ) { range = getSelection(); } if ( !range ) { range = getSelection(); }
range.collapse( true ); range.collapse( true );
if ( isInline( el ) ) { if ( isInline( el ) ) {
range._insertNode( el ); insertNodeInRange( range, el );
range.setStartAfter( el ); range.setStartAfter( el );
} else { } else {
// Get containing block node. // Get containing block node.
var splitNode = range.getStartBlock() || body, var splitNode = getStartBlockOfRange( range ) || body,
parent, nodeAfterSplit; parent, nodeAfterSplit;
// While at end of container node, move up DOM tree. // While at end of container node, move up DOM tree.
while ( splitNode !== body && !splitNode.nextSibling ) { while ( splitNode !== body && !splitNode.nextSibling ) {
@ -1424,7 +1426,7 @@ var insertElement = function ( el, range ) {
body.insertBefore( el, nodeAfterSplit ); body.insertBefore( el, nodeAfterSplit );
range.setStart( nodeAfterSplit, 0 ); range.setStart( nodeAfterSplit, 0 );
range.setStart( nodeAfterSplit, 0 ); range.setStart( nodeAfterSplit, 0 );
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
} else { } else {
body.appendChild( el ); body.appendChild( el );
// Insert blank line below block. // Insert blank line below block.
@ -1454,9 +1456,9 @@ var saveRangeToBookmark = function ( range ) {
}), }),
temp; temp;
range._insertNode( startNode ); insertNodeInRange( range, startNode );
range.collapse( false ); range.collapse( false );
range._insertNode( endNode ); insertNodeInRange( range, endNode );
// In a collapsed range, the start is sometimes inserted after the end! // In a collapsed range, the start is sometimes inserted after the end!
if ( startNode.compareDocumentPosition( endNode ) & if ( startNode.compareDocumentPosition( endNode ) &
@ -1508,7 +1510,7 @@ var getRangeAndRemoveBookmark = function ( range ) {
range.setEnd( _range.endContainer, _range.endOffset ); range.setEnd( _range.endContainer, _range.endOffset );
collapsed = range.collapsed; collapsed = range.collapsed;
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
if ( collapsed ) { if ( collapsed ) {
range.collapse( true ); range.collapse( true );
} }
@ -1638,7 +1640,7 @@ var hasFormat = function ( tag, attributes, range ) {
// Otherwise, check each text node at least partially contained within // Otherwise, check each text node at least partially contained within
// the selection and make sure all of them have the format we want. // the selection and make sure all of them have the format we want.
walker = new TreeWalker( root, SHOW_TEXT, function ( node ) { walker = new TreeWalker( root, SHOW_TEXT, function ( node ) {
return range.containsNode( node, true ) ? return isNodeContainedInRange( range, node, true ) ?
FILTER_ACCEPT : FILTER_SKIP; FILTER_ACCEPT : FILTER_SKIP;
}, false ); }, false );
@ -1661,7 +1663,7 @@ var addFormat = function ( tag, attributes, range ) {
if ( range.collapsed ) { if ( range.collapsed ) {
el = fixCursor( createElement( tag, attributes ) ); el = fixCursor( createElement( tag, attributes ) );
range._insertNode( el ); insertNodeInRange( range, el );
range.setStart( el.firstChild, el.firstChild.length ); range.setStart( el.firstChild, el.firstChild.length );
range.collapse( true ); range.collapse( true );
} }
@ -1678,7 +1680,7 @@ var addFormat = function ( tag, attributes, range ) {
range.commonAncestorContainer, range.commonAncestorContainer,
SHOW_TEXT, SHOW_TEXT,
function ( node ) { function ( node ) {
return range.containsNode( node, true ) ? return isNodeContainedInRange( range, node, true ) ?
FILTER_ACCEPT : FILTER_SKIP; FILTER_ACCEPT : FILTER_SKIP;
}, },
false false
@ -1741,7 +1743,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
} else { } else {
fixer = doc.createTextNode( '' ); fixer = doc.createTextNode( '' );
} }
range._insertNode( fixer ); insertNodeInRange( range, fixer );
} }
// Find block-level ancestor of selection // Find block-level ancestor of selection
@ -1760,7 +1762,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
examineNode = function ( node, exemplar ) { examineNode = function ( node, exemplar ) {
// If the node is completely contained by the range then // If the node is completely contained by the range then
// we're going to remove all formatting so ignore it. // we're going to remove all formatting so ignore it.
if ( range.containsNode( node, false ) ) { if ( isNodeContainedInRange( range, node, false ) ) {
return; return;
} }
@ -1769,7 +1771,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
// If not at least partially contained, wrap entire contents // If not at least partially contained, wrap entire contents
// in a clone of the tag we're removing and we're done. // in a clone of the tag we're removing and we're done.
if ( !range.containsNode( node, true ) ) { if ( !isNodeContainedInRange( range, node, true ) ) {
// Ignore bookmarks and empty text nodes // Ignore bookmarks and empty text nodes
if ( node.nodeName !== 'INPUT' && if ( node.nodeName !== 'INPUT' &&
( !isText || node.data ) ) { ( !isText || node.data ) ) {
@ -1800,7 +1802,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
}, },
formatTags = Array.prototype.filter.call( formatTags = Array.prototype.filter.call(
root.getElementsByTagName( tag ), function ( el ) { root.getElementsByTagName( tag ), function ( el ) {
return range.containsNode( el, true ) && return isNodeContainedInRange( range, el, true ) &&
hasTagAttributes( el, tag, attributes ); hasTagAttributes( el, tag, attributes );
} }
); );
@ -1912,8 +1914,8 @@ var forEachBlock = function ( fn, mutates, range ) {
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
} }
var start = range.getStartBlock(), var start = getStartBlockOfRange( range ),
end = range.getEndBlock(); end = getEndBlockOfRange( range );
if ( start && end ) { if ( start && end ) {
do { do {
if ( fn( start ) || start === end ) { break; } if ( fn( start ) || start === end ) { break; }
@ -1950,14 +1952,14 @@ var modifyBlocks = function ( modify, range ) {
} }
// 3. Expand range to block boundaries // 3. Expand range to block boundaries
range.expandToBlockBoundaries(); expandRangeToBlockBoundaries( range );
// 4. Remove range. // 4. Remove range.
range.moveBoundariesUpTree( body ); moveRangeBoundariesUpTree( range, body );
var frag = range._extractContents( body ); var frag = extractContentsOfRange( range, body );
// 5. Modify tree of fragment and reinsert. // 5. Modify tree of fragment and reinsert.
range._insertNode( modify( frag ) ); insertNodeInRange( range, modify( frag ) );
// 6. Merge containers at edges // 6. Merge containers at edges
if ( range.endOffset < range.endContainer.childNodes.length ) { if ( range.endOffset < range.endContainer.childNodes.length ) {
@ -2541,7 +2543,7 @@ addEventListener( isIE ? 'beforepaste' : 'paste', function ( event ) {
// Insert pasted data // Insert pasted data
if ( doPaste ) { if ( doPaste ) {
range.insertTreeFragment( frag ); insertTreeFragmentIntoRange( range, frag );
docWasChanged(); docWasChanged();
range.collapse( false ); range.collapse( false );
@ -2615,7 +2617,7 @@ var afterDelete = function () {
parent = getPreviousBlock( parent ); parent = getPreviousBlock( parent );
} }
fixCursor( parent ); fixCursor( parent );
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
setSelection( range ); setSelection( range );
updatePath( range ); updatePath( range );
} }
@ -2656,10 +2658,10 @@ var keyHandlers = {
// Selected text is overwritten, therefore delete the contents // Selected text is overwritten, therefore delete the contents
// to collapse selection. // to collapse selection.
if ( !range.collapsed ) { if ( !range.collapsed ) {
range._deleteContents(); deleteContentsOfRange( range );
} }
var block = range.getStartBlock(), var block = getStartBlockOfRange( range ),
tag = block ? block.nodeName : 'DIV', tag = block ? block.nodeName : 'DIV',
splitTag = tagAfterSplit[ tag ], splitTag = tagAfterSplit[ tag ],
nodeAfterSplit; nodeAfterSplit;
@ -2667,7 +2669,7 @@ var keyHandlers = {
// If this is a malformed bit of document, just play it safe // If this is a malformed bit of document, just play it safe
// and insert a <br>. // and insert a <br>.
if ( !block ) { if ( !block ) {
range._insertNode( createElement( 'BR' ) ); insertNodeInRange( range, createElement( 'BR' ) );
range.collapse( false ); range.collapse( false );
setSelection( range ); setSelection( range );
updatePath( range, true ); updatePath( range, true );
@ -2711,7 +2713,7 @@ var keyHandlers = {
} }
range.setStart( splitNode, splitOffset ); range.setStart( splitNode, splitOffset );
range.setEnd( splitNode, splitOffset ); range.setEnd( splitNode, splitOffset );
block = range.getStartBlock(); block = getStartBlockOfRange( range );
} }
if ( !block.textContent ) { if ( !block.textContent ) {
@ -2785,16 +2787,16 @@ var keyHandlers = {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
range._deleteContents(); deleteContentsOfRange( range );
setSelection( range ); setSelection( range );
updatePath( range, true ); updatePath( range, true );
} }
// If at beginning of block, merge with previous // If at beginning of block, merge with previous
else if ( range.startsAtBlockBoundary() ) { else if ( rangeDoesStartAtBlockBoundary( range ) ) {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
var current = range.getStartBlock(), var current = getStartBlockOfRange( range ),
previous = current && getPreviousBlock( current ); previous = current && getPreviousBlock( current );
// Must not be at the very beginning of the text area. // Must not be at the very beginning of the text area.
if ( previous ) { if ( previous ) {
@ -2851,16 +2853,16 @@ var keyHandlers = {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
range._deleteContents(); deleteContentsOfRange( range );
setSelection( range ); setSelection( range );
updatePath( range, true ); updatePath( range, true );
} }
// If at end of block, merge next into this block // If at end of block, merge next into this block
else if ( range.endsAtBlockBoundary() ) { else if ( rangeDoesEndAtBlockBoundary( range ) ) {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
var current = range.getStartBlock(), var current = getStartBlockOfRange( range ),
next = current && getNextBlock( current ); next = current && getNextBlock( current );
// Must not be at the very end of the text area. // Must not be at the very end of the text area.
if ( next ) { if ( next ) {
@ -3093,7 +3095,7 @@ editor = win.editor = {
}, },
getSelectedText: function () { getSelectedText: function () {
return getSelection().getTextContent(); return getTextContentInRange( getSelection() );
}, },
insertElement: chain( insertElement ), insertElement: chain( insertElement ),

File diff suppressed because one or more lines are too long

View file

@ -44,6 +44,21 @@
mergeContainers, mergeContainers,
createElement, createElement,
forEachTextNodeInRange,
getTextContentInRange,
insertNodeInRange,
extractContentsOfRange,
deleteContentsOfRange,
insertTreeFragmentIntoRange,
isNodeContainedInRange,
moveRangeBoundariesDownTree,
moveRangeBoundariesUpTree,
getStartBlockOfRange,
getEndBlockOfRange,
rangeDoesStartAtBlockBoundary,
rangeDoesEndAtBlockBoundary,
expandRangeToBlockBoundaries,
Range, Range,
top, top,
console, console,
@ -321,11 +336,11 @@ var insertElement = function ( el, range ) {
if ( !range ) { range = getSelection(); } if ( !range ) { range = getSelection(); }
range.collapse( true ); range.collapse( true );
if ( isInline( el ) ) { if ( isInline( el ) ) {
range._insertNode( el ); insertNodeInRange( range, el );
range.setStartAfter( el ); range.setStartAfter( el );
} else { } else {
// Get containing block node. // Get containing block node.
var splitNode = range.getStartBlock() || body, var splitNode = getStartBlockOfRange( range ) || body,
parent, nodeAfterSplit; parent, nodeAfterSplit;
// While at end of container node, move up DOM tree. // While at end of container node, move up DOM tree.
while ( splitNode !== body && !splitNode.nextSibling ) { while ( splitNode !== body && !splitNode.nextSibling ) {
@ -340,7 +355,7 @@ var insertElement = function ( el, range ) {
body.insertBefore( el, nodeAfterSplit ); body.insertBefore( el, nodeAfterSplit );
range.setStart( nodeAfterSplit, 0 ); range.setStart( nodeAfterSplit, 0 );
range.setStart( nodeAfterSplit, 0 ); range.setStart( nodeAfterSplit, 0 );
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
} else { } else {
body.appendChild( el ); body.appendChild( el );
// Insert blank line below block. // Insert blank line below block.
@ -370,9 +385,9 @@ var saveRangeToBookmark = function ( range ) {
}), }),
temp; temp;
range._insertNode( startNode ); insertNodeInRange( range, startNode );
range.collapse( false ); range.collapse( false );
range._insertNode( endNode ); insertNodeInRange( range, endNode );
// In a collapsed range, the start is sometimes inserted after the end! // In a collapsed range, the start is sometimes inserted after the end!
if ( startNode.compareDocumentPosition( endNode ) & if ( startNode.compareDocumentPosition( endNode ) &
@ -424,7 +439,7 @@ var getRangeAndRemoveBookmark = function ( range ) {
range.setEnd( _range.endContainer, _range.endOffset ); range.setEnd( _range.endContainer, _range.endOffset );
collapsed = range.collapsed; collapsed = range.collapsed;
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
if ( collapsed ) { if ( collapsed ) {
range.collapse( true ); range.collapse( true );
} }
@ -554,7 +569,7 @@ var hasFormat = function ( tag, attributes, range ) {
// Otherwise, check each text node at least partially contained within // Otherwise, check each text node at least partially contained within
// the selection and make sure all of them have the format we want. // the selection and make sure all of them have the format we want.
walker = new TreeWalker( root, SHOW_TEXT, function ( node ) { walker = new TreeWalker( root, SHOW_TEXT, function ( node ) {
return range.containsNode( node, true ) ? return isNodeContainedInRange( range, node, true ) ?
FILTER_ACCEPT : FILTER_SKIP; FILTER_ACCEPT : FILTER_SKIP;
}, false ); }, false );
@ -577,7 +592,7 @@ var addFormat = function ( tag, attributes, range ) {
if ( range.collapsed ) { if ( range.collapsed ) {
el = fixCursor( createElement( tag, attributes ) ); el = fixCursor( createElement( tag, attributes ) );
range._insertNode( el ); insertNodeInRange( range, el );
range.setStart( el.firstChild, el.firstChild.length ); range.setStart( el.firstChild, el.firstChild.length );
range.collapse( true ); range.collapse( true );
} }
@ -594,7 +609,7 @@ var addFormat = function ( tag, attributes, range ) {
range.commonAncestorContainer, range.commonAncestorContainer,
SHOW_TEXT, SHOW_TEXT,
function ( node ) { function ( node ) {
return range.containsNode( node, true ) ? return isNodeContainedInRange( range, node, true ) ?
FILTER_ACCEPT : FILTER_SKIP; FILTER_ACCEPT : FILTER_SKIP;
}, },
false false
@ -657,7 +672,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
} else { } else {
fixer = doc.createTextNode( '' ); fixer = doc.createTextNode( '' );
} }
range._insertNode( fixer ); insertNodeInRange( range, fixer );
} }
// Find block-level ancestor of selection // Find block-level ancestor of selection
@ -676,7 +691,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
examineNode = function ( node, exemplar ) { examineNode = function ( node, exemplar ) {
// If the node is completely contained by the range then // If the node is completely contained by the range then
// we're going to remove all formatting so ignore it. // we're going to remove all formatting so ignore it.
if ( range.containsNode( node, false ) ) { if ( isNodeContainedInRange( range, node, false ) ) {
return; return;
} }
@ -685,7 +700,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
// If not at least partially contained, wrap entire contents // If not at least partially contained, wrap entire contents
// in a clone of the tag we're removing and we're done. // in a clone of the tag we're removing and we're done.
if ( !range.containsNode( node, true ) ) { if ( !isNodeContainedInRange( range, node, true ) ) {
// Ignore bookmarks and empty text nodes // Ignore bookmarks and empty text nodes
if ( node.nodeName !== 'INPUT' && if ( node.nodeName !== 'INPUT' &&
( !isText || node.data ) ) { ( !isText || node.data ) ) {
@ -716,7 +731,7 @@ var removeFormat = function ( tag, attributes, range, partial ) {
}, },
formatTags = Array.prototype.filter.call( formatTags = Array.prototype.filter.call(
root.getElementsByTagName( tag ), function ( el ) { root.getElementsByTagName( tag ), function ( el ) {
return range.containsNode( el, true ) && return isNodeContainedInRange( range, el, true ) &&
hasTagAttributes( el, tag, attributes ); hasTagAttributes( el, tag, attributes );
} }
); );
@ -828,8 +843,8 @@ var forEachBlock = function ( fn, mutates, range ) {
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
} }
var start = range.getStartBlock(), var start = getStartBlockOfRange( range ),
end = range.getEndBlock(); end = getEndBlockOfRange( range );
if ( start && end ) { if ( start && end ) {
do { do {
if ( fn( start ) || start === end ) { break; } if ( fn( start ) || start === end ) { break; }
@ -866,14 +881,14 @@ var modifyBlocks = function ( modify, range ) {
} }
// 3. Expand range to block boundaries // 3. Expand range to block boundaries
range.expandToBlockBoundaries(); expandRangeToBlockBoundaries( range );
// 4. Remove range. // 4. Remove range.
range.moveBoundariesUpTree( body ); moveRangeBoundariesUpTree( range, body );
var frag = range._extractContents( body ); var frag = extractContentsOfRange( range, body );
// 5. Modify tree of fragment and reinsert. // 5. Modify tree of fragment and reinsert.
range._insertNode( modify( frag ) ); insertNodeInRange( range, modify( frag ) );
// 6. Merge containers at edges // 6. Merge containers at edges
if ( range.endOffset < range.endContainer.childNodes.length ) { if ( range.endOffset < range.endContainer.childNodes.length ) {
@ -1457,7 +1472,7 @@ addEventListener( isIE ? 'beforepaste' : 'paste', function ( event ) {
// Insert pasted data // Insert pasted data
if ( doPaste ) { if ( doPaste ) {
range.insertTreeFragment( frag ); insertTreeFragmentIntoRange( range, frag );
docWasChanged(); docWasChanged();
range.collapse( false ); range.collapse( false );
@ -1531,7 +1546,7 @@ var afterDelete = function () {
parent = getPreviousBlock( parent ); parent = getPreviousBlock( parent );
} }
fixCursor( parent ); fixCursor( parent );
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
setSelection( range ); setSelection( range );
updatePath( range ); updatePath( range );
} }
@ -1572,10 +1587,10 @@ var keyHandlers = {
// Selected text is overwritten, therefore delete the contents // Selected text is overwritten, therefore delete the contents
// to collapse selection. // to collapse selection.
if ( !range.collapsed ) { if ( !range.collapsed ) {
range._deleteContents(); deleteContentsOfRange( range );
} }
var block = range.getStartBlock(), var block = getStartBlockOfRange( range ),
tag = block ? block.nodeName : 'DIV', tag = block ? block.nodeName : 'DIV',
splitTag = tagAfterSplit[ tag ], splitTag = tagAfterSplit[ tag ],
nodeAfterSplit; nodeAfterSplit;
@ -1583,7 +1598,7 @@ var keyHandlers = {
// If this is a malformed bit of document, just play it safe // If this is a malformed bit of document, just play it safe
// and insert a <br>. // and insert a <br>.
if ( !block ) { if ( !block ) {
range._insertNode( createElement( 'BR' ) ); insertNodeInRange( range, createElement( 'BR' ) );
range.collapse( false ); range.collapse( false );
setSelection( range ); setSelection( range );
updatePath( range, true ); updatePath( range, true );
@ -1627,7 +1642,7 @@ var keyHandlers = {
} }
range.setStart( splitNode, splitOffset ); range.setStart( splitNode, splitOffset );
range.setEnd( splitNode, splitOffset ); range.setEnd( splitNode, splitOffset );
block = range.getStartBlock(); block = getStartBlockOfRange( range );
} }
if ( !block.textContent ) { if ( !block.textContent ) {
@ -1701,16 +1716,16 @@ var keyHandlers = {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
range._deleteContents(); deleteContentsOfRange( range );
setSelection( range ); setSelection( range );
updatePath( range, true ); updatePath( range, true );
} }
// If at beginning of block, merge with previous // If at beginning of block, merge with previous
else if ( range.startsAtBlockBoundary() ) { else if ( rangeDoesStartAtBlockBoundary( range ) ) {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
var current = range.getStartBlock(), var current = getStartBlockOfRange( range ),
previous = current && getPreviousBlock( current ); previous = current && getPreviousBlock( current );
// Must not be at the very beginning of the text area. // Must not be at the very beginning of the text area.
if ( previous ) { if ( previous ) {
@ -1767,16 +1782,16 @@ var keyHandlers = {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
range._deleteContents(); deleteContentsOfRange( range );
setSelection( range ); setSelection( range );
updatePath( range, true ); updatePath( range, true );
} }
// If at end of block, merge next into this block // If at end of block, merge next into this block
else if ( range.endsAtBlockBoundary() ) { else if ( rangeDoesEndAtBlockBoundary( range ) ) {
recordUndoState( range ); recordUndoState( range );
getRangeAndRemoveBookmark( range ); getRangeAndRemoveBookmark( range );
event.preventDefault(); event.preventDefault();
var current = range.getStartBlock(), var current = getStartBlockOfRange( range ),
next = current && getNextBlock( current ); next = current && getNextBlock( current );
// Must not be at the very end of the text area. // Must not be at the very end of the text area.
if ( next ) { if ( next ) {
@ -2009,7 +2024,7 @@ editor = win.editor = {
}, },
getSelectedText: function () { getSelectedText: function () {
return getSelection().getTextContent(); return getTextContentInRange( getSelection() );
}, },
insertElement: chain( insertElement ), insertElement: chain( insertElement ),

View file

@ -20,9 +20,7 @@
fixCursor, fixCursor,
split, split,
mergeWithBlock, mergeWithBlock,
mergeContainers, mergeContainers
Range
*/ */
/*jshint strict:false */ /*jshint strict:false */
@ -51,11 +49,11 @@ var getNodeAfter = function ( node, offset ) {
return node; return node;
}; };
var RangePrototype = Range.prototype; // ---
RangePrototype.forEachTextNode = function ( fn ) { var forEachTextNodeInRange = function ( range, fn ) {
var range = this.cloneRange(); range = range.cloneRange();
range.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
var startContainer = range.startContainer, var startContainer = range.startContainer,
endContainer = range.endContainer, endContainer = range.endContainer,
@ -71,9 +69,9 @@ RangePrototype.forEachTextNode = function ( fn ) {
( textnode = walker.nextNode() ) ) {} ( textnode = walker.nextNode() ) ) {}
}; };
RangePrototype.getTextContent = function () { var getTextContentInRange = function ( range ) {
var textContent = ''; var textContent = '';
this.forEachTextNode( function ( textnode, range ) { forEachTextNodeInRange( range, function ( textnode, range ) {
var value = textnode.data; var value = textnode.data;
if ( value && ( /\S/.test( value ) ) ) { if ( value && ( /\S/.test( value ) ) ) {
if ( textnode === range.endContainer ) { if ( textnode === range.endContainer ) {
@ -90,12 +88,12 @@ RangePrototype.getTextContent = function () {
// --- // ---
RangePrototype._insertNode = function ( node ) { var insertNodeInRange = function ( range, node ) {
// Insert at start. // Insert at start.
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
parent, children, childCount, afterSplit; parent, children, childCount, afterSplit;
// If part way through a text node, split it. // If part way through a text node, split it.
@ -104,7 +102,7 @@ RangePrototype._insertNode = function ( node ) {
children = parent.childNodes; children = parent.childNodes;
if ( startOffset === startContainer.length ) { if ( startOffset === startContainer.length ) {
startOffset = indexOf.call( children, startContainer ) + 1; startOffset = indexOf.call( children, startContainer ) + 1;
if ( this.collapsed ) { if ( range.collapsed ) {
endContainer = parent; endContainer = parent;
endOffset = startOffset; endOffset = startOffset;
} }
@ -138,20 +136,18 @@ RangePrototype._insertNode = function ( node ) {
endOffset += children.length - childCount; endOffset += children.length - childCount;
} }
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
return this;
}; };
RangePrototype._extractContents = function ( common ) { var extractContentsOfRange = function ( range, common ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset; endOffset = range.endOffset;
if ( !common ) { if ( !common ) {
common = this.commonAncestorContainer; common = range.commonAncestorContainer;
} }
if ( common.nodeType === TEXT_NODE ) { if ( common.nodeType === TEXT_NODE ) {
@ -170,28 +166,28 @@ RangePrototype._extractContents = function ( common ) {
startNode = next; startNode = next;
} }
this.setStart( common, endNode ? range.setStart( common, endNode ?
indexOf.call( common.childNodes, endNode ) : indexOf.call( common.childNodes, endNode ) :
common.childNodes.length ); common.childNodes.length );
this.collapse( true ); range.collapse( true );
fixCursor( common ); fixCursor( common );
return frag; return frag;
}; };
RangePrototype._deleteContents = function () { var deleteContentsOfRange = function ( range ) {
// Move boundaries up as much as possible to reduce need to split. // Move boundaries up as much as possible to reduce need to split.
this.moveBoundariesUpTree(); moveRangeBoundariesUpTree( range );
// Remove selected range // Remove selected range
this._extractContents(); extractContentsOfRange( range );
// If we split into two different blocks, merge the blocks. // If we split into two different blocks, merge the blocks.
var startBlock = this.getStartBlock(), var startBlock = getStartBlockOfRange( range ),
endBlock = this.getEndBlock(); endBlock = getEndBlockOfRange( range );
if ( startBlock && endBlock && startBlock !== endBlock ) { if ( startBlock && endBlock && startBlock !== endBlock ) {
mergeWithBlock( startBlock, endBlock, this ); mergeWithBlock( startBlock, endBlock, range );
} }
// Ensure block has necessary children // Ensure block has necessary children
@ -200,27 +196,25 @@ RangePrototype._deleteContents = function () {
} }
// Ensure body has a block-level element in it. // Ensure body has a block-level element in it.
var body = this.endContainer.ownerDocument.body, var body = range.endContainer.ownerDocument.body,
child = body.firstChild; child = body.firstChild;
if ( !child || child.nodeName === 'BR' ) { if ( !child || child.nodeName === 'BR' ) {
fixCursor( body ); fixCursor( body );
this.selectNodeContents( body.firstChild ); range.selectNodeContents( body.firstChild );
} }
// Ensure valid range (must have only block or inline containers) // Ensure valid range (must have only block or inline containers)
var isCollapsed = this.collapsed; var isCollapsed = range.collapsed;
this.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
if ( isCollapsed ) { if ( isCollapsed ) {
// Collapse // Collapse
this.collapse( true ); range.collapse( true );
} }
return this;
}; };
// --- // ---
RangePrototype.insertTreeFragment = function ( frag ) { var insertTreeFragmentIntoRange = function ( range, frag ) {
// Check if it's all inline content // Check if it's all inline content
var allInline = true, var allInline = true,
children = frag.childNodes, children = frag.childNodes,
@ -233,23 +227,23 @@ RangePrototype.insertTreeFragment = function ( frag ) {
} }
// Delete any selected content // Delete any selected content
if ( !this.collapsed ) { if ( !range.collapsed ) {
this._deleteContents(); deleteContentsOfRange( range );
} }
// Move range down into text ndoes // Move range down into text ndoes
this.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
// If inline, just insert at the current position. // If inline, just insert at the current position.
if ( allInline ) { if ( allInline ) {
this._insertNode( frag ); insertNodeInRange( range, frag );
this.collapse( false ); range.collapse( false );
} }
// Otherwise, split up to body, insert inline before and after split // Otherwise, split up to body, insert inline before and after split
// and insert block in between split, then merge containers. // and insert block in between split, then merge containers.
else { else {
var nodeAfterSplit = split( this.startContainer, this.startOffset, var nodeAfterSplit = split( range.startContainer, range.startOffset,
this.startContainer.ownerDocument.body ), range.startContainer.ownerDocument.body ),
nodeBeforeSplit = nodeAfterSplit.previousSibling, nodeBeforeSplit = nodeAfterSplit.previousSibling,
startContainer = nodeBeforeSplit, startContainer = nodeBeforeSplit,
startOffset = startContainer.childNodes.length, startOffset = startContainer.childNodes.length,
@ -305,17 +299,16 @@ RangePrototype.insertTreeFragment = function ( frag ) {
mergeContainers( nodeBeforeSplit ); mergeContainers( nodeBeforeSplit );
} }
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
this.moveBoundariesDownTree(); moveRangeBoundariesDownTree( range );
} }
}; };
// --- // ---
RangePrototype.containsNode = function ( node, partial ) { var isNodeContainedInRange = function ( range, node, partial ) {
var range = this, var nodeRange = node.ownerDocument.createRange();
nodeRange = node.ownerDocument.createRange();
nodeRange.selectNode( node ); nodeRange.selectNode( node );
@ -339,11 +332,11 @@ RangePrototype.containsNode = function ( node, partial ) {
} }
}; };
RangePrototype.moveBoundariesDownTree = function () { var moveRangeBoundariesDownTree = function ( range ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
child; child;
while ( startContainer.nodeType !== TEXT_NODE ) { while ( startContainer.nodeType !== TEXT_NODE ) {
@ -376,26 +369,24 @@ RangePrototype.moveBoundariesDownTree = function () {
// If collapsed, this algorithm finds the nearest text node positions // If collapsed, this algorithm finds the nearest text node positions
// *outside* the range rather than inside, but also it flips which is // *outside* the range rather than inside, but also it flips which is
// assigned to which. // assigned to which.
if ( this.collapsed ) { if ( range.collapsed ) {
this.setStart( endContainer, endOffset ); range.setStart( endContainer, endOffset );
this.setEnd( startContainer, startOffset ); range.setEnd( startContainer, startOffset );
} else { } else {
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
} }
return this;
}; };
RangePrototype.moveBoundariesUpTree = function ( common ) { var moveRangeBoundariesUpTree = function ( range, common ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
endContainer = this.endContainer, endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
parent; parent;
if ( !common ) { if ( !common ) {
common = this.commonAncestorContainer; common = range.commonAncestorContainer;
} }
while ( startContainer !== common && !startOffset ) { while ( startContainer !== common && !startOffset ) {
@ -411,16 +402,14 @@ RangePrototype.moveBoundariesUpTree = function ( common ) {
endContainer = parent; endContainer = parent;
} }
this.setStart( startContainer, startOffset ); range.setStart( startContainer, startOffset );
this.setEnd( endContainer, endOffset ); range.setEnd( endContainer, endOffset );
return this;
}; };
// 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.
RangePrototype.getStartBlock = function () { var getStartBlockOfRange = function ( range ) {
var container = this.startContainer, var container = range.startContainer,
block; block;
// If inline, get the containing block. // If inline, get the containing block.
@ -429,17 +418,17 @@ RangePrototype.getStartBlock = function () {
} else if ( isBlock( container ) ) { } else if ( isBlock( container ) ) {
block = container; block = container;
} else { } else {
block = getNodeBefore( container, this.startOffset ); block = getNodeBefore( container, range.startOffset );
block = getNextBlock( block ); block = getNextBlock( block );
} }
// Check the block actually intersects the range // Check the block actually intersects the range
return block && this.containsNode( block, true ) ? block : null; return block && isNodeContainedInRange( range, block, true ) ? block : null;
}; };
// Returns the last block at least partially contained by the range, // Returns the last 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.
RangePrototype.getEndBlock = function () { var getEndBlockOfRange = function ( range ) {
var container = this.endContainer, var container = range.endContainer,
block, child; block, child;
// If inline, get the containing block. // If inline, get the containing block.
@ -448,7 +437,7 @@ RangePrototype.getEndBlock = function () {
} else if ( isBlock( container ) ) { } else if ( isBlock( container ) ) {
block = container; block = container;
} else { } else {
block = getNodeAfter( container, this.endOffset ); block = getNodeAfter( container, range.endOffset );
if ( !block ) { if ( !block ) {
block = container.ownerDocument.body; block = container.ownerDocument.body;
while ( child = block.lastChild ) { while ( child = block.lastChild ) {
@ -459,12 +448,12 @@ RangePrototype.getEndBlock = function () {
} }
// Check the block actually intersects the range // Check the block actually intersects the range
return block && this.containsNode( block, true ) ? block : null; return block && isNodeContainedInRange( range, block, true ) ? block : null;
}; };
RangePrototype.startsAtBlockBoundary = function () { var rangeDoesStartAtBlockBoundary = function ( range ) {
var startContainer = this.startContainer, var startContainer = range.startContainer,
startOffset = this.startOffset, startOffset = range.startOffset,
parent, child; parent, child;
while ( isInline( startContainer ) ) { while ( isInline( startContainer ) ) {
@ -484,9 +473,9 @@ RangePrototype.startsAtBlockBoundary = function () {
return !startOffset; return !startOffset;
}; };
RangePrototype.endsAtBlockBoundary = function () { var rangeDoesEndAtBlockBoundary = function ( range ) {
var endContainer = this.endContainer, var endContainer = range.endContainer,
endOffset = this.endOffset, endOffset = range.endOffset,
length = getLength( endContainer ), length = getLength( endContainer ),
parent, child; parent, child;
@ -508,17 +497,15 @@ RangePrototype.endsAtBlockBoundary = function () {
return endOffset === length; return endOffset === length;
}; };
RangePrototype.expandToBlockBoundaries = function () { var expandRangeToBlockBoundaries = function ( range ) {
var start = this.getStartBlock(), var start = getStartBlockOfRange( range ),
end = this.getEndBlock(), end = getEndBlockOfRange( range ),
parent; parent;
if ( start && end ) { if ( start && end ) {
parent = start.parentNode; parent = start.parentNode;
this.setStart( parent, indexOf.call( parent.childNodes, start ) ); range.setStart( parent, indexOf.call( parent.childNodes, start ) );
parent = end.parentNode; parent = end.parentNode;
this.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 ); range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 );
} }
return this;
}; };