aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content_scripts/mode_edit.coffee12
-rw-r--r--content_scripts/mode_movement.coffee50
-rw-r--r--content_scripts/mode_visual.coffee45
-rw-r--r--lib/utils.coffee14
-rw-r--r--manifest.json1
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"