Created
June 13, 2011 01:28
-
-
Save tcr/1022198 to your computer and use it in GitHub Desktop.
Cross-Browser Selection Utilities
This file contains 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
######################################################################## | |
# Cross-Browser Selection Utilities | |
# Provides a 'selection' object with an API supporting | |
# all modern browsers (using window.getSelection()) | |
# and IE5+ (using TextRanges) | |
######################################################################## | |
root = this | |
if root.getSelection | |
# DOMSelection | |
root.selection = | |
hasSelection: (win) -> | |
return (sel = win.getSelection()) and sel.focusNode? and sel.anchorNode? | |
getOrigin: (win) -> | |
return null unless (sel = win.getSelection()) and sel.anchorNode? | |
return [sel.anchorNode, sel.anchorOffset] | |
getFocus: (win) -> | |
return null unless (sel = win.getSelection()) and sel.focusNode? | |
return [sel.focusNode, sel.focusOffset] | |
getStart: (win) -> | |
return null unless root.selection.hasSelection(win) | |
[n1, o1] = root.selection.getOrigin(win) | |
[n2, o2] = root.selection.getFocus(win) | |
if util.dom.isPreceding(n1, n2) or (n1 == n2 and o1 < o2) | |
return [n1, o1] | |
return [n2, o2] | |
getEnd: (win) -> | |
return null unless root.selection.hasSelection(win) | |
[n1, o1] = root.selection.getOrigin(win) | |
[n2, o2] = root.selection.getFocus(win) | |
if util.dom.isPreceding(n1, n2) or (n1 == n2 and o1 < o2) | |
return [n2, o2] | |
return [n1, o1] | |
setSelection: (win, orgn, orgo, focn, foco) -> | |
# not using Selection methods as IE9 doesn't support extend() | |
#win.getSelection()?.collapse(orgn, orgo) | |
#win.getSelection()?.extend(focn, foco) | |
r = win.document.createRange() | |
r.setStart(orgn, orgo) | |
r.setEnd(focn, foco) | |
try | |
win.getSelection()?.removeAllRanges() | |
catch e | |
# IE9 throws error sometimes | |
win.getSelection()?.addRange(r) | |
else if root.document.selection | |
# TextRanges (IE5+) | |
(-> | |
getBoundary = (doc, textRange, bStart) -> | |
# iterate backwards through parent element to find anchor location | |
cursorNode = doc.createElement('a') | |
cursor = textRange.duplicate() | |
cursor.collapse(bStart) | |
parent = cursor.parentElement() | |
loop | |
parent.insertBefore(cursorNode, cursorNode.previousSibling); | |
cursor.moveToElementText(cursorNode); | |
unless cursor.compareEndPoints((if bStart then 'StartToStart' else 'StartToEnd'), textRange) > 0 and | |
cursorNode.previousSibling? | |
break | |
# when we exceed or meet the cursor, we've found the node | |
if cursor.compareEndPoints((if bStart then 'StartToStart' else 'StartToEnd'), textRange) == -1 and | |
cursorNode.nextSibling | |
# data node | |
cursor.setEndPoint((if bStart then 'EndToStart' else 'EndToEnd'), textRange); | |
node = cursorNode.nextSibling | |
offset = cursor.text.length | |
else | |
# element | |
node = cursorNode.parentNode | |
offset = util.dom.getChildIndex(cursorNode) | |
cursorNode.parentNode.removeChild(cursorNode) | |
return [node, offset] | |
moveBoundary = (doc, textRange, bStart, node, offset) -> | |
# find anchor node and offset | |
textOffset = 0 | |
anchorNode = if util.dom.isText(node) then node else node.childNodes[offset] | |
anchorParent = if util.dom.isText(node) then node.parentNode else node | |
# visible data nodes need a text offset | |
if util.dom.isText(node) | |
textOffset = offset | |
# create a cursor element node to position range (since we can't select text nodes) | |
cursorNode = doc.createElement('a') | |
anchorParent.insertBefore(cursorNode, anchorNode or null) | |
cursor = doc.body.createTextRange() | |
cursor.moveToElementText(cursorNode) | |
cursorNode.parentNode.removeChild(cursorNode) | |
# move range | |
textRange.setEndPoint((if bStart then 'StartToStart' else 'EndToEnd'), cursor) | |
textRange[if bStart then 'moveStart' else 'moveEnd']('character', textOffset) | |
root.selection = | |
hasSelection: (win) -> | |
win.focus() | |
return false unless win.document.selection | |
range = win.document.selection.createRange() | |
return range && range.parentElement().document == win.document | |
getStart: (win) -> | |
win.focus() | |
return null unless root.selection.hasSelection(win) | |
range = win.document.selection.createRange() | |
return getBoundary(win.document, range, yes) | |
getEnd: (win) -> | |
win.focus() | |
return null unless root.selection.hasSelection(win) | |
range = win.document.selection.createRange() | |
return getBoundary(win.document, range, no) | |
# TextRange has no forward or backward indicator; | |
# just assume origin is start, focus end | |
getOrigin: (win) -> root.selection.getStart(win) | |
getFocus: (win) -> root.selection.getEnd(win) | |
setSelection: (win, orgn, orgo, focn, foco) -> | |
range = win.document.body.createTextRange() | |
# intentionally [end, start] order | |
moveBoundary(win.document, range, false, focn, foco) | |
moveBoundary(win.document, range, true, orgn, orgo) | |
range.select() | |
)() | |
else | |
# not supported | |
throw new Exception('Browser not supported: no selection support.') |
This file contains 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
var root; | |
root = this; | |
if (root.getSelection) { | |
root.selection = { | |
hasSelection: function(win) { | |
var sel; | |
return (sel = win.getSelection()) && (sel.focusNode != null) && (sel.anchorNode != null); | |
}, | |
getOrigin: function(win) { | |
var sel; | |
if (!((sel = win.getSelection()) && (sel.anchorNode != null))) { | |
return null; | |
} | |
return [sel.anchorNode, sel.anchorOffset]; | |
}, | |
getFocus: function(win) { | |
var sel; | |
if (!((sel = win.getSelection()) && (sel.focusNode != null))) { | |
return null; | |
} | |
return [sel.focusNode, sel.focusOffset]; | |
}, | |
getStart: function(win) { | |
var n1, n2, o1, o2, _ref, _ref2; | |
if (!root.selection.hasSelection(win)) { | |
return null; | |
} | |
_ref = root.selection.getOrigin(win), n1 = _ref[0], o1 = _ref[1]; | |
_ref2 = root.selection.getFocus(win), n2 = _ref2[0], o2 = _ref2[1]; | |
if (util.dom.isPreceding(n1, n2) || (n1 === n2 && o1 < o2)) { | |
return [n1, o1]; | |
} | |
return [n2, o2]; | |
}, | |
getEnd: function(win) { | |
var n1, n2, o1, o2, _ref, _ref2; | |
if (!root.selection.hasSelection(win)) { | |
return null; | |
} | |
_ref = root.selection.getOrigin(win), n1 = _ref[0], o1 = _ref[1]; | |
_ref2 = root.selection.getFocus(win), n2 = _ref2[0], o2 = _ref2[1]; | |
if (util.dom.isPreceding(n1, n2) || (n1 === n2 && o1 < o2)) { | |
return [n2, o2]; | |
} | |
return [n1, o1]; | |
}, | |
setSelection: function(win, orgn, orgo, focn, foco) { | |
var r, _ref, _ref2; | |
r = win.document.createRange(); | |
r.setStart(orgn, orgo); | |
r.setEnd(focn, foco); | |
try { | |
if ((_ref = win.getSelection()) != null) { | |
_ref.removeAllRanges(); | |
} | |
} catch (e) { | |
} | |
return (_ref2 = win.getSelection()) != null ? _ref2.addRange(r) : void 0; | |
} | |
}; | |
} else if (root.document.selection) { | |
(function() { | |
var getBoundary, moveBoundary; | |
getBoundary = function(doc, textRange, bStart) { | |
var cursor, cursorNode, node, offset, parent; | |
cursorNode = doc.createElement('a'); | |
cursor = textRange.duplicate(); | |
cursor.collapse(bStart); | |
parent = cursor.parentElement(); | |
while (true) { | |
parent.insertBefore(cursorNode, cursorNode.previousSibling); | |
cursor.moveToElementText(cursorNode); | |
if (!(cursor.compareEndPoints((bStart ? 'StartToStart' : 'StartToEnd'), textRange) > 0 && (cursorNode.previousSibling != null))) { | |
break; | |
} | |
} | |
if (cursor.compareEndPoints((bStart ? 'StartToStart' : 'StartToEnd'), textRange) === -1 && cursorNode.nextSibling) { | |
cursor.setEndPoint((bStart ? 'EndToStart' : 'EndToEnd'), textRange); | |
node = cursorNode.nextSibling; | |
offset = cursor.text.length; | |
} else { | |
node = cursorNode.parentNode; | |
offset = util.dom.getChildIndex(cursorNode); | |
} | |
cursorNode.parentNode.removeChild(cursorNode); | |
return [node, offset]; | |
}; | |
moveBoundary = function(doc, textRange, bStart, node, offset) { | |
var anchorNode, anchorParent, cursor, cursorNode, textOffset; | |
textOffset = 0; | |
anchorNode = util.dom.isText(node) ? node : node.childNodes[offset]; | |
anchorParent = util.dom.isText(node) ? node.parentNode : node; | |
if (util.dom.isText(node)) { | |
textOffset = offset; | |
} | |
cursorNode = doc.createElement('a'); | |
anchorParent.insertBefore(cursorNode, anchorNode || null); | |
cursor = doc.body.createTextRange(); | |
cursor.moveToElementText(cursorNode); | |
cursorNode.parentNode.removeChild(cursorNode); | |
textRange.setEndPoint((bStart ? 'StartToStart' : 'EndToEnd'), cursor); | |
return textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); | |
}; | |
return root.selection = { | |
hasSelection: function(win) { | |
var range; | |
win.focus(); | |
if (!win.document.selection) { | |
return false; | |
} | |
range = win.document.selection.createRange(); | |
return range && range.parentElement().document === win.document; | |
}, | |
getStart: function(win) { | |
var range; | |
win.focus(); | |
if (!root.selection.hasSelection(win)) { | |
return null; | |
} | |
range = win.document.selection.createRange(); | |
return getBoundary(win.document, range, true); | |
}, | |
getEnd: function(win) { | |
var range; | |
win.focus(); | |
if (!root.selection.hasSelection(win)) { | |
return null; | |
} | |
range = win.document.selection.createRange(); | |
return getBoundary(win.document, range, false); | |
}, | |
getOrigin: function(win) { | |
return root.selection.getStart(win); | |
}, | |
getFocus: function(win) { | |
return root.selection.getEnd(win); | |
}, | |
setSelection: function(win, orgn, orgo, focn, foco) { | |
var range; | |
range = win.document.body.createTextRange(); | |
moveBoundary(win.document, range, false, focn, foco); | |
moveBoundary(win.document, range, true, orgn, orgo); | |
return range.select(); | |
} | |
}; | |
})(); | |
} else { | |
throw new Exception('Browser not supported: no selection support.'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment