aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorStephen Blott2015-01-22 16:50:58 +0000
committerStephen Blott2015-01-23 09:53:49 +0000
commit256beee031efef70f4ee750044d9e697d66868bd (patch)
tree851b8aa8abdb3b5875d5caa52166d83f13ca89b8 /lib
parenteefe8c29b2410119412984301eba8c66dffda059 (diff)
downloadvimium-256beee031efef70f4ee750044d9e697d66868bd.tar.bz2
Visual/edit modes: develop edit mode.
- implement "i", "a". - fix "w" for edit mode. - try out "e" for enter edit mode. - initial implementation "o", "O" - Suppress backspace and delete. - Scroll in text areas.
Diffstat (limited to 'lib')
-rw-r--r--lib/dom_utils.coffee94
-rw-r--r--lib/handler_stack.coffee12
2 files changed, 103 insertions, 3 deletions
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 4f36e395..477abef2 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -231,5 +231,99 @@ DomUtils =
@remove()
false
+extend DomUtils,
+ # From: https://github.com/component/textarea-caret-position/blob/master/index.js
+ getCaretCoordinates: do ->
+ # The properties that we copy into a mirrored div.
+ # Note that some browsers, such as Firefox,
+ # do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
+ # so we have to do every single property specifically.
+ properties = [
+ 'direction', # RTL support
+ 'boxSizing',
+ 'width', # on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
+ 'height',
+ 'overflowX',
+ 'overflowY', # copy the scrollbar for IE
+
+ 'borderTopWidth',
+ 'borderRightWidth',
+ 'borderBottomWidth',
+ 'borderLeftWidth',
+
+ 'paddingTop',
+ 'paddingRight',
+ 'paddingBottom',
+ 'paddingLeft',
+
+ # https://developer.mozilla.org/en-US/docs/Web/CSS/font
+ 'fontStyle',
+ 'fontVariant',
+ 'fontWeight',
+ 'fontStretch',
+ 'fontSize',
+ 'fontSizeAdjust',
+ 'lineHeight',
+ 'fontFamily',
+
+ 'textAlign',
+ 'textTransform',
+ 'textIndent',
+ 'textDecoration', # might not make a difference, but better be safe
+
+ 'letterSpacing',
+ 'wordSpacing'
+ ]
+
+ `function (element, position, recalculate) {
+ // mirrored div
+ var div = document.createElement('div');
+ div.id = 'input-textarea-caret-position-mirror-div';
+ document.body.appendChild(div);
+
+ var style = div.style;
+ var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
+
+ // default textarea styles
+ style.whiteSpace = 'pre-wrap';
+ if (element.nodeName !== 'INPUT')
+ style.wordWrap = 'break-word'; // only for textarea-s
+
+ // position off-screen
+ style.position = 'absolute'; // required to return coordinates properly
+ style.visibility = 'hidden'; // not 'display: none' because we want rendering
+
+ // transfer the element's properties to the div
+ properties.forEach(function (prop) {
+ style[prop] = computed[prop];
+ });
+
+ style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
+
+ div.textContent = element.value.substring(0, position);
+ // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
+ if (element.nodeName === 'INPUT')
+ div.textContent = div.textContent.replace(/\s/g, "\u00a0");
+
+ var span = document.createElement('span');
+ // Wrapping must be replicated *exactly*, including when a long word gets
+ // onto the next line, with whitespace at the end of the line before (#7).
+ // The *only* reliable way to do that is to copy the *entire* rest of the
+ // textarea's content into the <span> created at the caret position.
+ // for inputs, just '.' would be enough, but why bother?
+ span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
+ div.appendChild(span);
+
+ var coordinates = {
+ top: span.offsetTop + parseInt(computed['borderTopWidth']),
+ left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
+ };
+
+ document.body.removeChild(div);
+
+ return coordinates;
+ }
+ `
+
root = exports ? window
root.DomUtils = DomUtils
diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee
index 76d835b7..9630759c 100644
--- a/lib/handler_stack.coffee
+++ b/lib/handler_stack.coffee
@@ -39,6 +39,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()
@@ -46,7 +47,7 @@ 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
@@ -81,7 +82,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.
@@ -93,7 +94,12 @@ class HandlerStack
when @restartBubbling then "rebubble"
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
root.HandlerStack = HandlerStack
root.handlerStack = new HandlerStack()