blob: ccd93870bca6e679eb83cb91f075008f64204ee0 (
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
80
81
|
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 not 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"]
isFocusable: (element) ->
(@isEditable(element) or @isEmbed element)
# Check whether insert mode is active. Also, activate insert mode if the current element is content
# editable.
isActive: ->
return true if @isInsertMode
# Some sites (e.g. inbox.google.com) change the contentEditable attribute on the fly (see #1245); and
# unfortunately, isEditable() is called *before* the change is made. Therefore, we need to re-check
# whether the active element is contentEditable.
@activate() if document.activeElement?.isContentEditable
@isInsertMode
activate: ->
unless @isInsertMode
@isInsertMode = true
@badge = "I"
Mode.updateBadge()
deactivate: ->
@isInsertMode = false
@badge = ""
Mode.updateBadge()
generateKeyHandler: (type) ->
(event) =>
return @continueBubbling unless @isActive()
return @stopBubblingAndTrue unless type == "keydown" and KeyboardUtils.isEscape event
# We're now exiting insert mode.
if @isEditable(event.srcElement) or @isEmbed 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()
@deactivate()
@suppressEvent
constructor: ->
super
name: "insert"
keydown: @generateKeyHandler "keydown"
keypress: @generateKeyHandler "keypress"
keyup: @generateKeyHandler "keyup"
@handlers.push handlerStack.push
focus: (event) =>
handlerStack.alwaysPropagate =>
if not @isInsertMode and @isFocusable event.target
@activate()
blur: (event) =>
handlerStack.alwaysPropagate =>
if @isInsertMode and @isFocusable event.target
@deactivate()
# We may already have been dropped into insert mode. So check.
Mode.updateBadge()
root = exports ? window
root.InsertMode = InsertMode
|