aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/mode_insert.coffee
blob: e68bf6abf0ff908db22a61f77c0d1355e5e17160 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class InsertMode extends Mode
  isInsertMode: false

  # Input or text elements are considered focusable and able to receieve their own keyboard events, and will
  # enter insert mode if focused. Also note that the "contentEditable" attribute can be set on any element
  # which makes it a rich text editor, like the notes on jjot.com.
  isEditable: (element) ->
    return true if element.isContentEditable
    nodeName = element.nodeName?.toLowerCase()
    # Use a blacklist instead of a whitelist because new form controls are still being implemented for html5.
    if nodeName == "input" and element.type and not element.type in ["radio", "checkbox"]
      return true
    nodeName in ["textarea", "select"]

  # Embedded elements like Flash and quicktime players can obtain focus but cannot be programmatically
  # unfocused.
  isEmbed: (element) ->
    element.nodeName?.toLowerCase() in ["embed", "object"]

  canEditElement: (element) ->
    element and (@isEditable(element) or @isEmbed element)

  # Check whether insert mode is active.  Also, activate insert mode if the current element is editable.
  isActive: ->
    return true if @isInsertMode
    # FIXME(smblott).  Is there a way to (safely) cache the results of these @canEditElement() calls?
    @activate() if @canEditElement document.activeElement
    @isInsertMode

  generateKeyHandler: (type) ->
    (event) =>
      return Mode.propagate unless @isActive()
      return handlerStack.passDirectlyToPage unless type == "keydown" and KeyboardUtils.isEscape event
      # We're now exiting insert mode.
      if @canEditElement event.srcElement
        # Remove the focus so the user can't just get himself back into insert mode by typing in the same input
        # box.
        # NOTE(smblott, 2014/12/22) Including embeds for .blur() here is experimental.  It appears to be the
        # right thing to do for most common use cases.  However, it could also cripple flash-based sites and
        # games.  See discussion in #1211 and #1194.
        event.srcElement.blur()
      @isInsertMode = false
      Mode.updateBadge()
      Mode.suppressPropagation

  activate: ->
    @isInsertMode = true
    Mode.updateBadge()

  # Override (and re-use) updateBadgeForMode() from Mode.updateBadgeForMode().  Use insert-mode badge only if
  # we're active and no mode higher in stack has already inserted a badge.
  updateBadgeForMode: (badge) ->
    @badge = if @isActive() then "I" else ""
    super badge

  checkModeState: ->
    previousState = @isInsertMode
    if @isActive() != previousState
      Mode.updateBadge()

  constructor: ->
    super
      name: "insert"
      badge: "I"
      keydown: @generateKeyHandler "keydown"
      keypress: @generateKeyHandler "keypress"
      keyup: @generateKeyHandler "keyup"

    @handlers.push handlerStack.push
      DOMActivate: => @checkModeState()
      focus: => @checkModeState()
      blur: => @checkModeState()

    # We may already have been dropped into insert mode.  So check.
    Mode.updateBadge()

root = exports ? window
root.InsertMode = InsertMode