This repository has been archived by the owner on Sep 23, 2020. It is now read-only.
/
Mediator.coffee
113 lines (98 loc) · 3.54 KB
/
Mediator.coffee
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class Mediator
constructor: (obj, @cascadeChannels=false) ->
@channels = {}
@installTo obj if obj
# ## Subscribe to a topic
#
# Parameters:
#
# - (String) topic - The topic name
# - (Function) callback - The function that gets called if an other module
# publishes to the specified topic
# - (Object) context - The context the function(s) belongs to
subscribe: (channel, fn, context=@) ->
@channels[channel] ?= []
that = @
if channel instanceof Array
@subscribe id, fn, context for id in channel
else if typeof channel is "object"
@subscribe k,v,fn for k,v of channel
else
return false unless typeof fn is "function"
return false unless typeof channel is "string"
subscription = { context: context, callback: fn }
(
attach: -> that.channels[channel].push subscription; @
detach: -> Mediator._rm that, channel, subscription.callback; @
).attach()
# Alias for subscribe
on: @::subscribe
# ## Unsubscribe from a topic
#
# Parameters:
#
# - (String) topic - The topic name
# - (Function) callback - The function that gets called if an other module
# publishes to the specified topic
unsubscribe: (ch, cb) ->
switch typeof ch
when "string"
Mediator._rm @,ch,cb if typeof cb is "function"
Mediator._rm @,ch if typeof cb is "undefined"
when "function" then Mediator._rm @,id,ch for id of @channels
when "undefined" then Mediator._rm @,id for id of @channels
when "object" then Mediator._rm @,id,null,ch for id of @channels
@
# ## Publish an event
#
# Parameters:
# (String) topic - The topic name
# (Object) data - The data that gets published
# (Object)
# callback: - callback metthod
# publishReference - If the data should be passed as a reference to
# the other modules this parameter has to be set
# to *true*.
# By default the data object gets copied so that
# other modules can't influence the original
# object.
publish: (channel, data, opt={}) ->
if typeof data is "function"
opt = data
data = undefined
return false unless typeof channel is "string"
subscribers = @channels[channel] or []
if typeof data is "object" and opt.publishReference isnt true
copy = util.clone data
tasks = for sub in subscribers then do (sub) ->
(next) ->
try
if (util.getArgumentNames sub.callback).length >= 3
sub.callback.apply sub.context, [(copy or data), channel, next]
else
next null, sub.callback.apply sub.context, [(copy or data), channel]
catch e
next e
util.runSeries tasks,((errors, results) ->
if errors
e = new Error (x.message for x in errors when x?).join '; '
opt? e), true
if @cascadeChannels and (chnls = channel.split('/')).length > 1
@publish chnls[0...-1].join('/'), data, opt
@
# Alias for publish
emit: @::publish
# ## Install Pub/Sub functions to an object
installTo: (obj) ->
if typeof obj is "object"
obj[k] = v for k,v of @
@
@_rm: (o, ch, cb, ctxt) ->
o.channels[ch] = (s for s in o.channels[ch] when (
if cb?
s.callback isnt cb
else if ctxt?
s.context isnt ctxt
else
s.context isnt o
))