-
-
Save guimello/7271093 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Browsers behave differently when dbl clicking to select | |
// a range. | |
// | |
// Chrome and IE use the text node that contains the selected text | |
// as the startContainer with an offset to exclude any whitespace | |
// characters at the start of the node. | |
// | |
// Firefox will use a text node *before* the selected text | |
// as the startContainer, with a positive offset set to the end | |
// of the node. If there is no previous sibling of the selected text | |
// or the previous sibling is not a text node, the node containing | |
// the selected text is used as the startContainer with a positive | |
// offset to exclude whitespace at the start of the node. | |
// | |
// Chrome uses the same text node as the startContainer for the | |
// endContainer, with a positive offset to exclude whitespace | |
// at the end of the node | |
// | |
// Firefox follows the mostly the same rules for the endContainer | |
// as it does for the startContainer. Any sibling text node | |
// *after* the selected text will be used as the endContainer, | |
// but with a 0 offset to exclude whitespace. If there is no next | |
// sibling or the next sibling is not a text node, the endContainer | |
// will be the same as the startContainer, with a positive offset | |
// to exclude any whitespace at the end of the node. | |
// | |
// IE will aways use a following text node as the endContainer, | |
// even if it is a child of a non-text sibling. The offset will | |
// include any whitespace characters at the beginning of the node. | |
// If there is no following text node, the endContainer will be the | |
// same as the startContainer, with a positive offset to include all | |
// contiguous whitespace characters. | |
// | |
// Examples: | |
// [] = startContainer, {} = endContainer, s:e = start/end offset | |
// | |
// given the snippet `Lorem <b>Ipsum</b> Dolor Sit`, | |
// | |
// dbl clicking to select the text `Lorem` | |
// Chrome: `[{Lorem }]<b>Ipsum</b> Dolor Sit` s0:e5 | |
// Firefox: `[{Lorem }]<b>Ipsum</b> Dolor Sit` s0:e5 | |
// IE: `[Lorem ]<b>{Ipsum}</b> Dolor Sit` s0:e0 | |
// | |
// dbl clicking to select the text `Ipsum` | |
// Chrome: `Lorem <b>[{Ipsum}]</b> Dolor Sit` s0:e5 | |
// Firefox: `[Lorem ]<b>Ipsum</b>{ Dolor Sit}` s6:e0 | |
// IE: `Lorem <b>[Ipsum]</b>{ Dolor Sit}` s0:e1 | |
// | |
// dbl clicking to select the text `Dolor` | |
// Chrome: `Lorem <b>Ipsum</b>[{ Dolor Sit}]` s1:e6 | |
// Firefox: `Lorem <b>Ipsum</b>[{ Dolor Sit}]` s1:e6 | |
// IE: `Lorem <b>Ipsum</b>[{ Dolor Sit}]` s1:e7 | |
// | |
// dbl clicking to select the text `Sit` | |
// Chrome: `Lorem <b>Ipsum</b>[{ Dolor Sit}]` s7:e10 | |
// Firefox: `Lorem <b>Ipsum</b>[{ Dolor Sit}]` s7:e10 | |
// IE: `Lorem <b>Ipsum</b>[{ Dolor Sit}]` s7:e10 | |
// | |
// Unable to find any specification on what the `correct` behavior | |
// is, but Chrome seems to be the most logical, so we'll use that | |
// as the target behavior for normalization. | |
var startNode = range.startNode(), | |
startOffset = range.startOffset(), | |
endNode = range.endNode(), | |
endOffset = range.endOffset(); | |
// If the startOffset is at the end of the startContainer | |
// assume the nextSibling contains the selected text (Firefox) | |
if (startNode.get('text').length === startOffset) { | |
startNode = range.startNode(startNode.get('nextSibling')); | |
} | |
// If the trimmed text of the endContainer is an empty string | |
// assume the previousSibling contains the selected text (IE, Firefox) | |
if (0 === Y.Lang.trim(endNode.get('text').substring(0, endOffset)).length) { | |
// IE will put the endNode in a child of a sibling node, so use the | |
// startNode if the current endNode doesn't have a previous sibling | |
endNode = range.endNode(endNode.get('previousSibling') || startNode); | |
// set the endOffset based on the node type | |
if (1 === endNode.get('nodeType')) { | |
endOffset = range.endOffset(endNode.get('childNodes').size()); | |
} else { | |
endOffset = range.endOffset(endNode.get('text').length); | |
} | |
} | |
// In most cases (Firefox being the sometimes exception), | |
// startContainer will already be the correct text node. If | |
// startContainer is an element node, unset it so the traverse | |
// function can set it. | |
if (1 === startNode.get('nodeType')) { | |
startNode = null; | |
} | |
// traverse the range and find the text nodes we want | |
// to use for the start/end containers | |
range.traverse(function(node) { | |
var text = node.get('text'); | |
if (!text.length || 1 === node.get('nodeType')) { | |
// don't want empty or element nodes | |
return; | |
} | |
if (!startNode) { | |
startNode = node; | |
// set the offset to exclude leading whitespace | |
startOffset = text.length - Y.Lang.trimLeft(text).length; | |
} | |
endNode = node; | |
// set the endOffset to the first occurrence of whitespace | |
// after the startOffset | |
endOffset = text.indexOf(' ', startOffset); | |
if (-1 === endOffset) { | |
// no whitespace after the startOffset, use the whole | |
// length of the text as the endOffset | |
endOffset = text.length; | |
} | |
}); | |
range.startNode(startNode, startOffset); | |
range.endNode(endNode, endOffset); | |
this.selection.select(range); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment