   if (!activatedElement || !isRendered(activatedElement))
     activatedElement = document.body
 
-  elementAmount = getDimension activatedElement, direction, amount
-  elementAmount *= factor
+  element = findScrollableElement activatedElement, direction, amount, factor
+  elementAmount = factor * getDimension element, direction, amount
+  doScrollBy element, direction, elementAmount, true
 
-  doScrollBy direction, elementAmount, true
-
-root.scrollTo = (direction, pos, wantSmooth=false) ->
+root.scrollTo = (direction, pos, wantSmooth = false) ->
   return unless document.body
 
   if (!activatedElement || !isRendered(activatedElement))
     activatedElement = document.body
 
-  # Find the deepest scrollable element which would move if we scrolled it.  This is the element which
-  # ensureScrollChange will scroll.
-  # TODO(smblott) We're pretty much copying what ensureScrollChange does here.  Refactor.
-  element = activatedElement
-  axisName = scrollProperties[direction].axisName
-  while element != document.body and
-    (getDimension(element, direction, pos) == element[axisName] or not isScrollable element, direction)
-      element = element.parentElement || document.body
-
-  amount = getDimension(element,direction,pos) - element[axisName]
-  doScrollBy direction, amount, wantSmooth
+  element = findScrollableElement activatedElement, direction, pos
+  amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName]
+  doScrollBy element, direction, amount, wantSmooth
 
 # TODO refactor and put this together with the code in getVisibleClientRect
 isRendered = (element) ->

    amount

# Test whether element should be scrolled.
isScrollable = (element, direction) ->
  # Elements with `overflow: hidden` should not be scrolled.
  overflow = window.getComputedStyle(element).getPropertyValue("overflow-#{direction}")
  return false if overflow == "hidden"
  return true

# Chrome does not report scrollHeight accurately for nodes with pseudo-elements of height 0 (bug 110149).
# Therefore we cannot figure out if we have scrolled to the bottom of an element by testing if scrollTop +
# clientHeight == scrollHeight. So just try to increase scrollTop blindly -- if it fails we know we have
# reached the end of the content.
ensureScrollChange = (direction, changeFn) ->
  axisName = scrollProperties[direction].axisName
  element = activatedElement
  progress = 0
  loop
    oldScrollValue = element[axisName]
    changeFn(element, axisName) if isScrollable element, direction
    progress += element[axisName] - oldScrollValue
    break unless element[axisName] == oldScrollValue && element != document.body
    # we may have an orphaned element. if so, just scroll the body element.
    element = element.parentElement || document.body

  # if the activated element has been scrolled completely offscreen, subsequent changes in its scroll
  # position will not provide any more visual feedback to the user. therefore we deactivate it so that
  # subsequent scrolls only move the parent element.
  rect = activatedElement.getBoundingClientRect()
  if (rect.bottom < 0 || rect.top > window.innerHeight || rect.right < 0 || rect.left > window.innerWidth)
    activatedElement = element
  # Return the amount by which the scroll position has changed.
  return progress

# Scroll by a relative amount in some direction, possibly smoothly.
# The constants below seem to roughly match chrome's scroll speeds for both short and long scrolls.
# TODO(smblott) For very-long scrolls, chrome implements a soft landing; we don't.
doScrollBy = do ->
  interval = 10 # Update interval (in ms).
  duration = 120 # This must be a multiple of interval (also in ms).

    # Ensure we have a multiple of interval.
    return interval * Math.round (extra / interval)

  (element, direction, amount, wantSmooth) ->
    axisName = scrollProperties[direction].axisName
    clearTimer()

    unless wantSmooth and settings.get "smoothScroll"
      return performScroll element, axisName, amount

    requiredTicks = (duration + calculateExtraDuration amount) / interval
    # Round away from 0, so that we don't leave any scroll amount unscrolled.
    delta = (if 0 <= amount then Math.ceil else Math.floor)(amount / requiredTicks)

    if delta
      ticks = 0
      ticker = ->
        if performScroll(element, axisName, delta, false) != delta or ++ticks == requiredTicks
          # One final call of performScroll to check the visibility of the activated element.
          performScroll(element, axisName, 0, true)
          clearTimer()

      timer = setInterval ticker, interval
      ticker()

# scroll the active element in :direction by :amount * :factor.
# :factor is needed because :amount can take on string values, which scrollBy converts to element dimensions.
