diff options
| -rw-r--r-- | content_scripts/mode_edit.coffee | 12 | ||||
| -rw-r--r-- | content_scripts/mode_movement.coffee | 50 | ||||
| -rw-r--r-- | content_scripts/mode_visual.coffee | 45 | ||||
| -rw-r--r-- | lib/utils.coffee | 14 | ||||
| -rw-r--r-- | manifest.json | 1 | 
5 files changed, 108 insertions, 14 deletions
| diff --git a/content_scripts/mode_edit.coffee b/content_scripts/mode_edit.coffee index 32f2e796..0b17044a 100644 --- a/content_scripts/mode_edit.coffee +++ b/content_scripts/mode_edit.coffee @@ -1,11 +1,12 @@ -class EditMode extends Mode +class EditMode extends Movement    @activeElements = []    constructor: (options = {}) ->      defaults =        name: "edit"        exitOnEscape: true +      alterMethod: "move"        keydown: (event) => if @isActive() then @handleKeydown event else @continueBubbling        keypress: (event) => if @isActive() then @handleKeypress event else @continueBubbling        keyup: (event) => if @isActive() then @handleKeyup event else @continueBubbling @@ -14,9 +15,12 @@ class EditMode extends Mode      if @element and DomUtils.isEditable @element        super extend defaults, options -  handleKeydown: (event) -> @suppressEvent -  handleKeypress: (event) -> @suppressEvent -  handleKeyup: (event) -> @suppressEvent +  handleKeydown: (event) -> +    @stopBubblingAndTrue +  handleKeypress: (event) -> +    @suppressEvent +  handleKeyup: (event) -> +    @stopBubblingAndTrue    isActive: ->      document.activeElement and DomUtils.isDOMDescendant @element, document.activeElement diff --git a/content_scripts/mode_movement.coffee b/content_scripts/mode_movement.coffee new file mode 100644 index 00000000..c81b698a --- /dev/null +++ b/content_scripts/mode_movement.coffee @@ -0,0 +1,50 @@ + +class Movement extends Mode +  movements: +    h: "backward character" +    l: "forward character" +    k: "backward line" +    j: "forward line" +    b: "backward word" +    e: "forward word" + +  constructor: (options) -> +    @countPrefix = "" +    @alterMethod = options.alterMethod +    super options + +    isNumberKey = (keyChar) -> +      keyChar.length == 1 and "0" <= keyChar <= "9" + +    @push +      keydown: (event) => @alwaysContinueBubbling => +        unless event.metaKey or event.ctrlKey or event.altKey +          keyChar = KeyboardUtils.getKeyChar event +          @countPrefix += keyChar if isNumberKey keyChar +      keyup: (event) => @alwaysContinueBubbling => +        # FIXME(smblott).  Need to revisit these test.  They do not cover all cases correctly. +        unless event.metaKey or event.ctrlKey or event.altKey or event.keyCode == keyCodes.shiftKey +          keyChar = KeyboardUtils.getKeyChar event +          if keyChar and not isNumberKey keyChar +            @countPrefix = "" + +  move: (keyChar) -> +    if @movements[keyChar] +      Utils.suppressor.suppress Movement, => +        countPrefix = if 0 < @countPrefix.length then parseInt @countPrefix else 1 +        @countPrefix = "" +        for [0...countPrefix] +          if "string" == typeof @movements[keyChar] +            window.getSelection().modify @alterMethod, @movements[keyChar].split(/\s+/)... +          else if "function" == typeof @movements[keyChar] +            @movements[keyChar]() +      Utils.suppressor.unlessSuppressed Movement, => @postMove?() + +  isMoveChar: (event, keyChar) -> +    return false if event.metaKey or event.ctrlKey or event.altKey +    @movements[keyChar] + +# setTimeout (-> new Movement {}), 500 + +root = exports ? window +root.Movement = Movement diff --git a/content_scripts/mode_visual.coffee b/content_scripts/mode_visual.coffee index 2580106d..36501f3e 100644 --- a/content_scripts/mode_visual.coffee +++ b/content_scripts/mode_visual.coffee @@ -1,20 +1,45 @@ -class VisualMode extends Mode -  constructor: (element=null) -> -    super +class VisualMode extends Movement +  constructor: (options = {}) -> +    defaults =        name: "visual"        badge: "V"        exitOnEscape: true -      exitOnBlur: element - -      keydown: (event) => -        return @suppressEvent +      exitOnBlur: options.targetElement +      alterMethod: "extend" +      keydown: (event) => @handleKeyEvent event, KeyboardUtils.getKeyChar event +      keyup: (event) => @handleKeyEvent event, KeyboardUtils.getKeyChar event        keypress: (event) => -        return @suppressEvent +        keyChar = String.fromCharCode event.charCode +        @handleKeyEvent event, keyChar, => @move keyChar + +    super extend defaults, options + +  handleKeyEvent: (event, keyChar, func = ->) -> +    if event.metaKey or event.ctrlKey or event.altKey +      @stopBubblingAndTrue +    else if event.type == "keypress" and @isMoveChar event, keyChar +      func keyChar +      @suppressEvent +    else if @handleVisualModeKey(keyChar) or @isMoveChar event, keyChar +      DomUtils.suppressPropagation +      @stopBubblingAndTrue +    else if KeyboardUtils.isPrintable event +      @suppressEvent +    else +      @stopBubblingAndTrue -      keyup: (event) => -        return @suppressEvent +  handleVisualModeKey: (keyChar) -> +    switch keyChar +      when "y" +        chrome.runtime.sendMessage +          handler: "copyToClipboard" +          data: window.getSelection().toString() +        @exit() +        true +      else +        false  root = exports ? window  root.VisualMode = VisualMode diff --git a/lib/utils.coffee b/lib/utils.coffee index 661f7e84..f8eb5457 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -152,6 +152,20 @@ Utils =    # locale-sensitive uppercase detection    hasUpperCase: (s) -> s.toLowerCase() != s +  # Allow a function call to be suppressed.  Use Utils.suppress.unlessSuppressed to call a function, unless it +  # is suppressed via the given key.  Use Utils.suppressor.suppress to call a function while suppressing the +  # given key. +  suppressor: do -> +    suppressed = {} + +    suppress: (key, func) -> +      suppressed[key] = if suppressed[key]? then suppressed[key] + 1 else 1 +      func() +      suppressed[key] -= 1 + +    unlessSuppressed: (key, func) -> +      func() unless suppressed[key]? and 0 < suppressed[key] +  # This creates a new function out of an existing function, where the new function takes fewer arguments. This  # allows us to pass around functions instead of functions + a partial list of arguments.  Function::curry = -> diff --git a/manifest.json b/manifest.json index e8b0f8ee..23c9c1e3 100644 --- a/manifest.json +++ b/manifest.json @@ -47,6 +47,7 @@               "content_scripts/mode_insert.js",               "content_scripts/mode_passkeys.js",               "content_scripts/mode_find.js", +             "content_scripts/mode_movement.js",               "content_scripts/mode_visual.js",               "content_scripts/mode_edit.js",               "content_scripts/vimium_frontend.js" | 
