diff options
| -rw-r--r-- | background_scripts/settings.coffee | 1 | ||||
| -rw-r--r-- | content_scripts/scroller.coffee | 83 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 12 | ||||
| -rw-r--r-- | pages/options.coffee | 1 | ||||
| -rw-r--r-- | pages/options.html | 9 | 
5 files changed, 85 insertions, 21 deletions
| diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee index d6e8fcde..f68a51d7 100644 --- a/background_scripts/settings.coffee +++ b/background_scripts/settings.coffee @@ -61,6 +61,7 @@ root.Settings = Settings =    # or strings    defaults:      scrollStepSize: 60 +    smoothScroll: false      keyMappings: "# Insert your prefered key mappings here."      linkHintCharacters: "sadfjklewcmpgh"      linkHintNumbers: "0123456789" diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee index b3a14c78..4d1109c9 100644 --- a/content_scripts/scroller.coffee +++ b/content_scripts/scroller.coffee @@ -5,8 +5,10 @@ window.Scroller = root = {}  # input elements. This mechanism allows us to decide whether to scroll a div or to scroll the whole document.  #  activatedElement = null +settings = null -root.init = -> +root.init = (frontendSettings) -> +  settings = frontendSettings    handlerStack.push DOMActivate: -> activatedElement = event.target  scrollProperties = @@ -36,11 +38,13 @@ getDimension = (el, direction, name) ->  ensureScrollChange = (direction, changeFn) ->    axisName = scrollProperties[direction].axisName    element = activatedElement +  progress = 0    loop      oldScrollValue = element[axisName]      # Elements with `overflow: hidden` should not be scrolled.      overflow = window.getComputedStyle(element).getPropertyValue("overflow-#{direction}")      changeFn(element, axisName) unless overflow == "hidden" +    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 @@ -51,6 +55,53 @@ ensureScrollChange = (direction, changeFn) ->    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). +  fudgeFactor = 25 +  timer = null + +  clearTimer = -> +    if timer +      clearInterval timer +      timer = null + +  # Allow a bit longer for longer scrolls. +  calculateExtraDuration = (amount) -> +    extra = fudgeFactor * Math.log Math.abs amount +    # Ensure we have a multiple of interval. +    return interval * Math.round (extra / interval) + +  scroller = (direction,amount) -> +    return ensureScrollChange direction, (element, axisName) -> element[axisName] += amount + +  (direction,amount,wantSmooth) -> +    clearTimer() + +    unless wantSmooth and settings.get "smoothScroll" +      scroller direction, amount +      return + +    requiredTicks = (duration + calculateExtraDuration amount) / interval +    # Round away from 0, so that we don't leave any requested scroll amount unscrolled. +    rounder = (if 0 <= amount then Math.ceil else Math.floor) +    delta = rounder(amount / requiredTicks) + +    ticks = 0 +    ticker = -> +      # If we haven't scrolled by the expected amount, then we've hit the top, bottom or side of the activated +      # element, so stop scrolling. +      if scroller(direction, delta) != delta or ++ticks == requiredTicks +        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. @@ -66,26 +117,28 @@ root.scrollBy = (direction, amount, factor = 1) ->    if (!activatedElement || !isRendered(activatedElement))      activatedElement = document.body -  ensureScrollChange direction, (element, axisName) -> -    if Utils.isString amount -      elementAmount = getDimension element, direction, amount -    else -      elementAmount = amount -    elementAmount *= factor -    element[axisName] += elementAmount +  if Utils.isString amount +    elementAmount = getDimension activatedElement, direction, amount +  else +    elementAmount = amount +  elementAmount *= factor + +  doScrollBy direction, elementAmount, true -root.scrollTo = (direction, pos) -> +root.scrollTo = (direction, pos, wantSmooth=false) ->    return unless document.body    if (!activatedElement || !isRendered(activatedElement))      activatedElement = document.body -  ensureScrollChange direction, (element, axisName) -> -    if Utils.isString pos -      elementPos = getDimension element, direction, pos -    else -      elementPos = pos -    element[axisName] = elementPos +  if Utils.isString pos +    elementPos = getDimension activatedElement, direction, pos +  else +    elementPos = pos +  axisName = scrollProperties[direction].axisName +  elementAmount = elementPos - activatedElement[axisName] + +  doScrollBy direction, elementAmount, wantSmooth  # TODO refactor and put this together with the code in getVisibleClientRect  isRendered = (element) -> diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 118f985e..57503565 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -49,7 +49,7 @@ settings =    loadedValues: 0    valuesToLoad: ["scrollStepSize", "linkHintCharacters", "linkHintNumbers", "filterLinkHints", "hideHud",      "previousPatterns", "nextPatterns", "findModeRawQuery", "regexFindMode", "userDefinedLinkHintCss", -    "helpDialog_showAdvancedCommands"] +    "helpDialog_showAdvancedCommands", "smoothScroll"]    isLoaded: false    eventListeners: {} @@ -101,7 +101,7 @@ initializePreDomReady = ->    settings.addEventListener("load", LinkHints.init.bind(LinkHints))    settings.load() -  Scroller.init() +  Scroller.init settings    checkIfEnabledForUrl() @@ -227,10 +227,10 @@ window.focusThisFrame = (shouldHighlight) ->      setTimeout((-> document.body.style.border = borderWas), 200)  extend window, -  scrollToBottom: -> Scroller.scrollTo "y", "max" -  scrollToTop: -> Scroller.scrollTo "y", 0 -  scrollToLeft: -> Scroller.scrollTo "x", 0 -  scrollToRight: -> Scroller.scrollTo "x", "max" +  scrollToBottom: -> Scroller.scrollTo "y", "max", true +  scrollToTop: -> Scroller.scrollTo "y", 0, true +  scrollToLeft: -> Scroller.scrollTo "x", 0, true +  scrollToRight: -> Scroller.scrollTo "x", "max", true    scrollUp: -> Scroller.scrollBy "y", -1 * settings.get("scrollStepSize")    scrollDown: -> Scroller.scrollBy "y", settings.get("scrollStepSize")    scrollPageUp: -> Scroller.scrollBy "y", "viewSize", -1/2 diff --git a/pages/options.coffee b/pages/options.coffee index f5968eb9..3474bcba 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -196,6 +196,7 @@ document.addEventListener "DOMContentLoaded", ->      previousPatterns: NonEmptyTextOption      regexFindMode: CheckBoxOption      scrollStepSize: NumberOption +    smoothScroll: CheckBoxOption      searchEngines: TextOption      searchUrl: NonEmptyTextOption      userDefinedLinkHintCss: TextOption diff --git a/pages/options.html b/pages/options.html index 4f037ba5..d6ce2764 100644 --- a/pages/options.html +++ b/pages/options.html @@ -360,6 +360,15 @@ unmapAll              </td>            </tr>            <tr> +            <td class="caption"></td> +            <td verticalAlign="top" class="booleanOption"> +              <label> +                <input id="smoothScroll" type="checkbox"/> +                Use smooth scrolling. +              </label> +            </td> +          </tr> +          <tr>              <td class="caption">Previous patterns</td>              <td verticalAlign="top">                  <div class="help"> | 
