mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 23:40:35 -05:00
parent
1b9606452e
commit
e6ae25b589
3 changed files with 81 additions and 57 deletions
|
@ -1700,7 +1700,7 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
// If the range is collapsed we simply insert the node by wrapping
|
// If the range is collapsed we simply insert the node by wrapping
|
||||||
// it round the range and focus it.
|
// it round the range and focus it.
|
||||||
var el, walker, startContainer, endContainer, startOffset, endOffset,
|
var el, walker, startContainer, endContainer, startOffset, endOffset,
|
||||||
textNode, needsFormat;
|
node, needsFormat;
|
||||||
|
|
||||||
if ( range.collapsed ) {
|
if ( range.collapsed ) {
|
||||||
el = fixCursor( this.createElement( tag, attributes ) );
|
el = fixCursor( this.createElement( tag, attributes ) );
|
||||||
|
@ -1712,16 +1712,21 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
// partially selected nodes) and if they're not already formatted
|
// partially selected nodes) and if they're not already formatted
|
||||||
// correctly we wrap them in the appropriate tag.
|
// correctly we wrap them in the appropriate tag.
|
||||||
else {
|
else {
|
||||||
// We don't want to apply formatting twice so we check each text
|
|
||||||
// node to see if it has an ancestor with the formatting already.
|
|
||||||
// Create an iterator to walk over all the text nodes under this
|
// Create an iterator to walk over all the text nodes under this
|
||||||
// ancestor which are in the range and not already formatted
|
// ancestor which are in the range and not already formatted
|
||||||
// correctly.
|
// correctly.
|
||||||
|
//
|
||||||
|
// In Blink/WebKit, empty blocks may have no text nodes, just a <br>.
|
||||||
|
// Therefore we wrap this in the tag as well, as this will then cause it
|
||||||
|
// to apply when the user types something in the block, which is
|
||||||
|
// presumably what was intended.
|
||||||
walker = new TreeWalker(
|
walker = new TreeWalker(
|
||||||
range.commonAncestorContainer,
|
range.commonAncestorContainer,
|
||||||
SHOW_TEXT,
|
SHOW_TEXT|SHOW_ELEMENT,
|
||||||
function ( node ) {
|
function ( node ) {
|
||||||
return isNodeContainedInRange( range, node, true );
|
return ( node.nodeType === TEXT_NODE ||
|
||||||
|
node.nodeName === 'BR' ) &&
|
||||||
|
isNodeContainedInRange( range, node, true );
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -1733,41 +1738,53 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
endContainer = range.endContainer;
|
endContainer = range.endContainer;
|
||||||
endOffset = range.endOffset;
|
endOffset = range.endOffset;
|
||||||
|
|
||||||
// Make sure we start inside a text node.
|
// Make sure we start with a valid node.
|
||||||
walker.currentNode = startContainer;
|
walker.currentNode = startContainer;
|
||||||
if ( startContainer.nodeType !== TEXT_NODE ) {
|
if ( !walker.filter( startContainer ) ) {
|
||||||
startContainer = walker.nextNode();
|
startContainer = walker.nextNode();
|
||||||
startOffset = 0;
|
startOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
// If there are no interesting nodes in the selection, abort
|
||||||
textNode = walker.currentNode;
|
if ( !startContainer ) {
|
||||||
needsFormat = !getNearest( textNode, tag, attributes );
|
return range;
|
||||||
if ( needsFormat ) {
|
|
||||||
if ( textNode === endContainer &&
|
|
||||||
textNode.length > endOffset ) {
|
|
||||||
textNode.splitText( endOffset );
|
|
||||||
}
|
}
|
||||||
if ( textNode === startContainer && startOffset ) {
|
|
||||||
textNode = textNode.splitText( startOffset );
|
do {
|
||||||
|
node = walker.currentNode;
|
||||||
|
needsFormat = !getNearest( node, tag, attributes );
|
||||||
|
if ( needsFormat ) {
|
||||||
|
// <br> can never be a container node, so must have a text node
|
||||||
|
// if node == (end|start)Container
|
||||||
|
if ( node === endContainer && node.length > endOffset ) {
|
||||||
|
node.splitText( endOffset );
|
||||||
|
}
|
||||||
|
if ( node === startContainer && startOffset ) {
|
||||||
|
node = node.splitText( startOffset );
|
||||||
if ( endContainer === startContainer ) {
|
if ( endContainer === startContainer ) {
|
||||||
endContainer = textNode;
|
endContainer = node;
|
||||||
endOffset -= startOffset;
|
endOffset -= startOffset;
|
||||||
}
|
}
|
||||||
startContainer = textNode;
|
startContainer = node;
|
||||||
startOffset = 0;
|
startOffset = 0;
|
||||||
}
|
}
|
||||||
el = this.createElement( tag, attributes );
|
el = this.createElement( tag, attributes );
|
||||||
replaceWith( textNode, el );
|
replaceWith( node, el );
|
||||||
el.appendChild( textNode );
|
el.appendChild( node );
|
||||||
}
|
}
|
||||||
} while ( walker.nextNode() );
|
} while ( walker.nextNode() );
|
||||||
|
|
||||||
// Make sure we finish inside a text node. Otherwise offset may have
|
// If we don't finish inside a text node, offset may have changed.
|
||||||
// changed.
|
|
||||||
if ( endContainer.nodeType !== TEXT_NODE ) {
|
if ( endContainer.nodeType !== TEXT_NODE ) {
|
||||||
endContainer = textNode;
|
if ( node.nodeType === TEXT_NODE ) {
|
||||||
endOffset = textNode.length;
|
endContainer = node;
|
||||||
|
endOffset = node.length;
|
||||||
|
} else {
|
||||||
|
// If <br>, we must have just wrapped it, so it must have only
|
||||||
|
// one child
|
||||||
|
endContainer = node.parentNode;
|
||||||
|
endOffset = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now set the selection to as it was before
|
// Now set the selection to as it was before
|
||||||
|
@ -2557,17 +2574,7 @@ var cleanupBRs = function ( root ) {
|
||||||
|
|
||||||
proto._ensureBottomLine = function () {
|
proto._ensureBottomLine = function () {
|
||||||
var body = this._body,
|
var body = this._body,
|
||||||
last;
|
last = body.lastElementChild;
|
||||||
// Safari (+others?) adds white-space text nodes to the end of <body>
|
|
||||||
// for no apparent reason. Remove them, since they're semantically
|
|
||||||
// meaningless.
|
|
||||||
while ( last = body.lastChild ) {
|
|
||||||
if ( last.nodeType === TEXT_NODE && !notWS.test( last.data ) ) {
|
|
||||||
body.removeChild( last );
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !last || last.nodeName !== this.defaultBlockTag || !isBlock( last ) ) {
|
if ( !last || last.nodeName !== this.defaultBlockTag || !isBlock( last ) ) {
|
||||||
body.appendChild( this.createDefaultBlock() );
|
body.appendChild( this.createDefaultBlock() );
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -610,7 +610,7 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
// If the range is collapsed we simply insert the node by wrapping
|
// If the range is collapsed we simply insert the node by wrapping
|
||||||
// it round the range and focus it.
|
// it round the range and focus it.
|
||||||
var el, walker, startContainer, endContainer, startOffset, endOffset,
|
var el, walker, startContainer, endContainer, startOffset, endOffset,
|
||||||
textNode, needsFormat;
|
node, needsFormat;
|
||||||
|
|
||||||
if ( range.collapsed ) {
|
if ( range.collapsed ) {
|
||||||
el = fixCursor( this.createElement( tag, attributes ) );
|
el = fixCursor( this.createElement( tag, attributes ) );
|
||||||
|
@ -622,16 +622,21 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
// partially selected nodes) and if they're not already formatted
|
// partially selected nodes) and if they're not already formatted
|
||||||
// correctly we wrap them in the appropriate tag.
|
// correctly we wrap them in the appropriate tag.
|
||||||
else {
|
else {
|
||||||
// We don't want to apply formatting twice so we check each text
|
|
||||||
// node to see if it has an ancestor with the formatting already.
|
|
||||||
// Create an iterator to walk over all the text nodes under this
|
// Create an iterator to walk over all the text nodes under this
|
||||||
// ancestor which are in the range and not already formatted
|
// ancestor which are in the range and not already formatted
|
||||||
// correctly.
|
// correctly.
|
||||||
|
//
|
||||||
|
// In Blink/WebKit, empty blocks may have no text nodes, just a <br>.
|
||||||
|
// Therefore we wrap this in the tag as well, as this will then cause it
|
||||||
|
// to apply when the user types something in the block, which is
|
||||||
|
// presumably what was intended.
|
||||||
walker = new TreeWalker(
|
walker = new TreeWalker(
|
||||||
range.commonAncestorContainer,
|
range.commonAncestorContainer,
|
||||||
SHOW_TEXT,
|
SHOW_TEXT|SHOW_ELEMENT,
|
||||||
function ( node ) {
|
function ( node ) {
|
||||||
return isNodeContainedInRange( range, node, true );
|
return ( node.nodeType === TEXT_NODE ||
|
||||||
|
node.nodeName === 'BR' ) &&
|
||||||
|
isNodeContainedInRange( range, node, true );
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -643,41 +648,53 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
endContainer = range.endContainer;
|
endContainer = range.endContainer;
|
||||||
endOffset = range.endOffset;
|
endOffset = range.endOffset;
|
||||||
|
|
||||||
// Make sure we start inside a text node.
|
// Make sure we start with a valid node.
|
||||||
walker.currentNode = startContainer;
|
walker.currentNode = startContainer;
|
||||||
if ( startContainer.nodeType !== TEXT_NODE ) {
|
if ( !walker.filter( startContainer ) ) {
|
||||||
startContainer = walker.nextNode();
|
startContainer = walker.nextNode();
|
||||||
startOffset = 0;
|
startOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
// If there are no interesting nodes in the selection, abort
|
||||||
textNode = walker.currentNode;
|
if ( !startContainer ) {
|
||||||
needsFormat = !getNearest( textNode, tag, attributes );
|
return range;
|
||||||
if ( needsFormat ) {
|
|
||||||
if ( textNode === endContainer &&
|
|
||||||
textNode.length > endOffset ) {
|
|
||||||
textNode.splitText( endOffset );
|
|
||||||
}
|
}
|
||||||
if ( textNode === startContainer && startOffset ) {
|
|
||||||
textNode = textNode.splitText( startOffset );
|
do {
|
||||||
|
node = walker.currentNode;
|
||||||
|
needsFormat = !getNearest( node, tag, attributes );
|
||||||
|
if ( needsFormat ) {
|
||||||
|
// <br> can never be a container node, so must have a text node
|
||||||
|
// if node == (end|start)Container
|
||||||
|
if ( node === endContainer && node.length > endOffset ) {
|
||||||
|
node.splitText( endOffset );
|
||||||
|
}
|
||||||
|
if ( node === startContainer && startOffset ) {
|
||||||
|
node = node.splitText( startOffset );
|
||||||
if ( endContainer === startContainer ) {
|
if ( endContainer === startContainer ) {
|
||||||
endContainer = textNode;
|
endContainer = node;
|
||||||
endOffset -= startOffset;
|
endOffset -= startOffset;
|
||||||
}
|
}
|
||||||
startContainer = textNode;
|
startContainer = node;
|
||||||
startOffset = 0;
|
startOffset = 0;
|
||||||
}
|
}
|
||||||
el = this.createElement( tag, attributes );
|
el = this.createElement( tag, attributes );
|
||||||
replaceWith( textNode, el );
|
replaceWith( node, el );
|
||||||
el.appendChild( textNode );
|
el.appendChild( node );
|
||||||
}
|
}
|
||||||
} while ( walker.nextNode() );
|
} while ( walker.nextNode() );
|
||||||
|
|
||||||
// Make sure we finish inside a text node. Otherwise offset may have
|
// If we don't finish inside a text node, offset may have changed.
|
||||||
// changed.
|
|
||||||
if ( endContainer.nodeType !== TEXT_NODE ) {
|
if ( endContainer.nodeType !== TEXT_NODE ) {
|
||||||
endContainer = textNode;
|
if ( node.nodeType === TEXT_NODE ) {
|
||||||
endOffset = textNode.length;
|
endContainer = node;
|
||||||
|
endOffset = node.length;
|
||||||
|
} else {
|
||||||
|
// If <br>, we must have just wrapped it, so it must have only
|
||||||
|
// one child
|
||||||
|
endContainer = node.parentNode;
|
||||||
|
endOffset = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now set the selection to as it was before
|
// Now set the selection to as it was before
|
||||||
|
|
Loading…
Reference in a new issue