aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/dom_utils.coffee64
-rw-r--r--lib/handler_stack.coffee15
-rw-r--r--lib/keyboard_utils.coffee9
-rw-r--r--lib/utils.coffee17
4 files changed, 100 insertions, 5 deletions
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 4f36e395..2ae9412e 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -231,5 +231,69 @@ DomUtils =
@remove()
false
+ simulateTextEntry: (element, text) ->
+ event = document.createEvent "TextEvent"
+ event.initTextEvent "textInput", true, true, null, text
+ element.dispatchEvent event
+
+ # Adapted from: http://roysharon.com/blog/37.
+ # This finds the element containing the selection focus.
+ getElementWithFocus: (selection, backwards) ->
+ r = t = selection.getRangeAt 0
+ if selection.type == "Range"
+ r = t.cloneRange()
+ r.collapse backwards
+ t = r.startContainer
+ t = t.childNodes[r.startOffset] if t.nodeType == 1
+ o = t
+ o = o.previousSibling while o and o.nodeType != 1
+ t = o || t?.parentNode
+ t
+
+ # This calculates the caret coordinates within an input element. It is used by edit mode to calculate the
+ # caret position for scrolling. It creates a hidden div contain a mirror of element, and all of the text
+ # from element up to position, then calculates the scroll position.
+ # From: https://github.com/component/textarea-caret-position/blob/master/index.js
+ getCaretCoordinates: do ->
+ # The properties that we copy to the mirrored div.
+ properties = [
+ 'direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY',
+ 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth',
+ 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
+ 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust',
+ 'lineHeight', 'fontFamily',
+ 'textAlign', 'textTransform', 'textIndent', 'textDecoration',
+ 'letterSpacing', 'wordSpacing' ]
+
+ (element, position) ->
+ div = document.createElement "div"
+ div.id = "vimium-input-textarea-caret-position-mirror-div"
+ document.body.appendChild div
+
+ style = div.style
+ computed = getComputedStyle element
+
+ style.whiteSpace = "pre-wrap"
+ style.wordWrap = "break-word" if element.nodeName.toLowerCase() != "input"
+ style.position = "absolute"
+ style.visibility = "hidden"
+ style[prop] = computed[prop] for prop in properties
+ style.overflow = "hidden"
+
+ div.textContent = element.value.substring 0, position
+ if element.nodeName.toLowerCase() == "input"
+ div.textContent = div.textContent.replace /\s/g, "\u00a0"
+
+ span = document.createElement "span"
+ span.textContent = element.value.substring(position) || "."
+ div.appendChild span
+
+ coordinates =
+ top: span.offsetTop + parseInt computed["borderTopWidth"]
+ left: span.offsetLeft + parseInt computed["borderLeftWidth"]
+
+ document.body.removeChild div
+ coordinates
+
root = exports ? window
root.DomUtils = DomUtils
diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee
index 3d635005..b0fefc7d 100644
--- a/lib/handler_stack.coffee
+++ b/lib/handler_stack.coffee
@@ -38,6 +38,7 @@ class HandlerStack
# @stopBubblingAndTrue.
bubbleEvent: (type, event) ->
@eventNumber += 1
+ eventNumber = @eventNumber
# We take a copy of the array in order to avoid interference from concurrent removes (for example, to
# avoid calling the same handler twice, because elements have been spliced out of the array by remove).
for handler in @stack[..].reverse()
@@ -45,13 +46,15 @@ class HandlerStack
if handler?.id and handler[type]
@currentId = handler.id
result = handler[type].call @, event
- @logResult type, event, handler, result if @debug
+ @logResult eventNumber, type, event, handler, result if @debug
if not result
DomUtils.suppressEvent event if @isChromeEvent event
return false
return true if result == @stopBubblingAndTrue
return false if result == @stopBubblingAndFalse
return @bubbleEvent type, event if result == @restartBubbling
+ else
+ @logResult eventNumber, type, event, handler, "skip" if @debug
true
remove: (id = @currentId) ->
@@ -80,7 +83,7 @@ class HandlerStack
false
# Debugging.
- logResult: (type, event, handler, result) ->
+ logResult: (eventNumber, type, event, handler, result) ->
# FIXME(smblott). Badge updating is too noisy, so we filter it out. However, we do need to look at how
# many badge update events are happening. It seems to be more than necessary. We also filter out
# registerKeyQueue as unnecessarily noisy and not particularly helpful.
@@ -90,9 +93,15 @@ class HandlerStack
when @stopBubblingAndTrue then "stop/true"
when @stopBubblingAndFalse then "stop/false"
when @restartBubbling then "rebubble"
+ when "skip" then "skip"
when true then "continue"
label ||= if result then "continue/truthy" else "suppress"
- console.log "#{@eventNumber}", type, handler._name, label
+ console.log "#{eventNumber}", type, handler._name, label
+
+ show: ->
+ console.log "#{@eventNumber}:"
+ for handler in @stack[..].reverse()
+ console.log " ", handler._name
# For tests only.
reset: ->
diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee
index 693c9b1c..5c95680c 100644
--- a/lib/keyboard_utils.coffee
+++ b/lib/keyboard_utils.coffee
@@ -1,6 +1,6 @@
KeyboardUtils =
keyCodes:
- { ESC: 27, backspace: 8, deleteKey: 46, enter: 13, space: 32, shiftKey: 16, ctrlKey: 17, f1: 112,
+ { ESC: 27, backspace: 8, deleteKey: 46, enter: 13, ctrlEnter: 10, space: 32, shiftKey: 16, ctrlKey: 17, f1: 112,
f12: 123, tab: 9, downArrow: 40, upArrow: 38 }
keyNames:
@@ -59,7 +59,12 @@ KeyboardUtils =
# identify any of chrome's own keyboard shortcuts as printable.
isPrintable: (event) ->
return false if event.metaKey or event.ctrlKey or event.altKey
- @getKeyChar(event)?.length == 1
+ keyChar =
+ if event.type == "keypress"
+ String.fromCharCode event.charCode
+ else
+ @getKeyChar event
+ keyChar.length == 1
KeyboardUtils.init()
diff --git a/lib/utils.coffee b/lib/utils.coffee
index 661f7e84..64c87842 100644
--- a/lib/utils.coffee
+++ b/lib/utils.coffee
@@ -152,6 +152,23 @@ Utils =
# locale-sensitive uppercase detection
hasUpperCase: (s) -> s.toLowerCase() != s
+ # Give objects (including elements) distinct identities.
+ getIdentity: do ->
+ identities = []
+
+ (obj) ->
+ index = identities.indexOf obj
+ if index < 0
+ index = identities.length
+ identities.push obj
+ "identity-" + index
+
+ # Return a copy of object, but with some of its properties omitted.
+ copyObjectOmittingProperties: (obj, properties...) ->
+ obj = extend {}, obj
+ delete obj[property] for property in properties
+ obj
+
# 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 = ->