     @setDOMRange(range)
     range
 
-  getLocationRangeAtPoint: ([pageX, pageY]) ->
-    if document.caretPositionFromPoint
-      {offsetNode, offset} = document.caretPositionFromPoint(pageX, pageY)
-      domRange = document.createRange()
-      domRange.setStart(offsetNode, offset)
+  # Selection observer delegate
 
-    else if document.caretRangeFromPoint
-      domRange = document.caretRangeFromPoint(pageX, pageY)
+  selectionDidChange: (domRange) ->
+    @updateCurrentLocationRange(domRange)
+    @delegate?.locationDidChange?(@currentLocationRange)
 
-    else if document.body.createTextRange
-      range = document.body.createTextRange()
-      range.moveToPoint(pageX, pageY)
-      range.select()
-      return @updateCurrentLocationRange()
+  # Private
 
-    if domRange
-      @createLocationRangeFromDOMRange(domRange)
-
-  getPointAtEndOfSelection: ->
-    if range = @getDOMRange()
-      rects = range.getClientRects()
-      if rects.length > 0
-        rect = rects[rects.length - 1]
-
-        pageX = rect.right
-        pageY = rect.top + rect.height / 2
-
-        if @clientRectIsRelativeToBody()
-          pageX -= document.body.scrollLeft
-          pageY -= document.body.scrollTop
-
-        [pageX, pageY]
+  updateCurrentLocationRange: (domRange) ->
+    domRange ?= @getDOMRange()
+    @currentLocationRange = @createLocationRangeFromDOMRange(domRange)
 
   getDOMRange: ->
     selection = window.getSelection()

    else
      NodeFilter.FILTER_SKIP

  # ClientRect position properties should be relative to the viewport,
  # but in some browsers (like mobile Safari), they're relative to the body.
  clientRectIsRelativeToBody: memoize ->

    selection = window.getSelection()
    selection.removeAllRanges()
    selection.addRange(range)

  createLocationRangeFromDOMRange: (range) ->
    return unless range?
