blob: a19c3df08f0820e011ff710d708cd1817a441c5b (
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
82
83
84
85
86
87
88
89
|
count = 0
class Mode
# Static members.
@modes: []
@current: -> Mode.modes[0]
# Constants; readable shortcuts for event-handler return values.
continueBubbling: true
suppressEvent: false
stopBubblingAndTrue: handlerStack.stopBubblingAndTrue
stopBubblingAndFalse: handlerStack.stopBubblingAndFalse
# Default values.
name: ""
badge: ""
keydown: (event) => @continueBubbling
keypress: (event) => @continueBubbling
keyup: (event) => @continueBubbling
constructor: (options) ->
Mode.modes.unshift @
extend @, options
@count = ++count
console.log @count, "create:", @name
@handlers = []
@handlers.push handlerStack.push
keydown: @keydown
keypress: @keypress
keyup: @keyup
updateBadge: (badge) => handlerStack.alwaysContinueBubbling => @chooseBadge badge
exit: ->
console.log @count, "exit:", @name
handlerStack.remove handlerId for handlerId in @handlers
Mode.modes = Mode.modes.filter (mode) => mode != @
Mode.updateBadge()
# The badge is chosen by bubbling an "updateBadge" event down the handler stack allowing each mode the
# opportunity to choose a badge. chooseBadge, here, is the default: choose the current mode's badge unless
# one has already been chosen. This is overridden in sub-classes.
chooseBadge: (badge) ->
badge.badge ||= @badge
# Static method. Used externally and internally to initiate bubbling of an updateBadge event and to send
# the resulting badge to the background page. We only update the badge if this document has the focus, and
# the document's body isn't a frameset.
@updateBadge: ->
if document.hasFocus()
unless document.body?.tagName.toLowerCase() == "frameset"
badge = {badge: ""}
handlerStack.bubbleEvent "updateBadge", badge
chrome.runtime.sendMessage({ handler: "setBadge", badge: badge.badge })
# Temporarily install a mode.
@runIn: (mode, func) ->
mode = new mode()
func()
mode.exit()
# A SingletonMode is a Mode of which there may be at most one instance (of @singleton) active at any one time.
# New instances cancel previous instances on startup.
class SingletonMode extends Mode
@instances: {}
exit: ->
delete SingletonMode.instances[@singleton]
super()
constructor: (@singleton, options={}) ->
SingletonMode.instances[@singleton].exit() if SingletonMode.instances[@singleton]
SingletonMode.instances[@singleton] = @
super options
# MultiMode is a collection of modes which are installed or uninstalled together.
class MultiMode extends Mode
constructor: (modes...) ->
@modes = (new mode() for mode in modes)
super {name: "multimode"}
exit: ->
mode.exit() for mode in modes
root = exports ? window
root.Mode = Mode
root.SingletonMode = SingletonMode
root.MultiMode = MultiMode
|