-
Notifications
You must be signed in to change notification settings - Fork 1
/
mediator.js
234 lines (217 loc) · 6.31 KB
/
mediator.js
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
(function(){
/*****************************************************
// js-mediator
// Mark Marijnissen
// https://github.com/markmarijnissen/js-mediator
******************************************************/
// Mediator object
var Mediator = {};
// Global Module register
var Modules = {};
// List of callbacks when waiting for specific modules
var ModuleCallbackList = [];
// List of ForEachCallbackList (all modules)
var ForEachCallbackList = [];
// Modules that are already coupled/grouped
var Connected = {};
/**
* Mediator.register(name,module)
*
* Register a module or instance
*/
Mediator.register = function MediatorRegister(name,object){
// validate if name is a string
if(typeof name !== 'string'){
throw new Error('Name must be a string!');
}
// validate if module exists
if(typeof object === 'undefined'){
throw new Error('Mediator.register takes two arguments');
}
// set 'isInstance': When name starts with lowercase letter
var isInstance = name[0] === name[0].toLowerCase();
// if instance
if(isInstance){
// create array if needed
if(!Modules[name]) Modules[name] = [];
// push instance to array
Modules[name].push(object);
// if module
} else {
// check if module name already exists
if(Modules[name]){
throw new Error('Module '+name+' already exists!');
}
// store module
Modules[name] = object;
// Invoke 'mediate' callbacks
for(var callbackIndex = ModuleCallbackList.length-1; callbackIndex >= 0; callbackIndex--){
var item = ModuleCallbackList[callbackIndex];
// it this callback waiting for current module?
var moduleIndex = item.waitFor.indexOf(name);
// if so...
if(moduleIndex > -1){
// ...remove module from 'wait' list
item.waitFor.splice(moduleIndex,1);
// Are we finished loading all modules?
if(item.waitFor.length === 0){
// map names to actual modules
var modules = getModules(item.modules);
// invoke callback
item.callback.apply(null,modules);
// remove callback from list
ModuleCallbackList.splice(callbackIndex,1);
}
}
}
}
ForEachCallbackList.forEach(function(callback){
callback(object,name);
});
return object;
};
/**
* Mediator.connect(modules,callback)
*
* @param {Array:string} array of module names to wait for
* @param {Function} callback
*
* Waits until modules are created, then fires callback.
*
* Example: RouterMediator.js
*
* Mediator.connect(['Router','PageController'],function(router,page) {
* router.on('change',page.update)
* })
*/
Mediator.connect = function MediatorConnect(modules,callback){
claimed = modules.filter(function(name){
var available = !!Connected[name];
if(available) Connected[name] = true;
return available;
});
if(claimed.length > 0){
throw new Error('Cannot group modules '+claimed.join(',')+': They are already coupled!');
}
var waitFor = modules.filter(function(name){ return !Modules[name]; });
// All modules are already loaded! Invoke immediatly
if(waitFor.length === 0){
callback.apply(null,getModules(modules));
// Waiting for modules. Invoke later.
} else {
ModuleCallbackList.push({
waitFor:waitFor,
modules:modules,
callback:callback
});
}
return Mediator;
};
/**
* Mediator.forEach([filter],[modules],fn)
*
* Execute a function for every module and instance
*
* @param {string} filter: instance name (optional)
* @param {array:string} modules: list of modules (optional)
* @param {Function} fn: forEach callback
*
* Examples:
*
* Mediator.forEach('page',['App'],function(page,name,App){
* App.addPage(page);
* })
*
* Mediator.forEach(['TwitterFeed'],function(module,name,TwitterFeed){
* if(module.onTweet) {
* TwitterFeed.addTweetListener(module.onTweet)
* }
* })
*
* Mediator.forEach(function(module,name){
* console.log('registered:',name);
* });
*/
Mediator.forEach = function MediatorForEach(filter,moduleNames,fn){
if(Array.isArray(filter) && typeof moduleNames === 'function'){
fn = moduleNames;
moduleNames = filter;
filter = null;
}
// forEach(fn)
if(typeof filter === 'function'){
fn = filter;
moduleNames = [];
filter = null;
}
// forEach(filter,fn)
if(typeof moduleNames === 'function'){
fn = moduleNames;
moduleNames = [];
}
if(typeof fn !== 'function'){
throw new Error('Callback is not a function!');
}
Mediator.connect(moduleNames,function(modules){
modules = Array.prototype.slice.apply(arguments);
var callback = function EnhanceForEachCallback(module,name){
if(filter === false || filter === name){
fn.apply(null,[module,name].concat(modules));
}
};
// Push callback to list (for modules that are registered in future)
ForEachCallbackList.push(callback);
// Call callback for modules that are already registered
Object.keys(Modules).forEach(function(name){
// Get module(s)
var modules = Modules[name];
// If Module
if(!Array.isArray(modules)) {
callback(modules,name);
// If Instance
} else {
modules.forEach(function(module){
callback(module,name);
});
}
});
});
return Mediator;
};
/**
* Group Modules
*
* A new Module is created with `name`.
* The grouped modules cannot be connected anymore -
* the group encapsulates the modules.
*
* @param {string} name
* @param {Array<string>} moduleNames
* @param {Function} callback(module,module,module,....)
* @return {Mediator}
*/
Mediator.group = function MediatorGroup(name,moduleNames,callback){
if(name[0] !== name[0].toUpperCase()){
throw new Error('Group is a Module, so the name should start with UpperCase.');
}
Mediator.connect(moduleNames,function MediatorGroupCallback(){
var module = {};
module = callback.apply(module,arguments) || module;
Mediator.register(name,module);
});
return Mediator;
};
/**
* Helper function to map module names to actual modules
* @param {Array:string} names
* @return {Array:object} modules
*/
function getModules(names){
return names.map(function(name){
return Modules[name];
});
}
Mediator.module = Modules;
window.Mediator = Mediator;
if(module && module.exports) module.exports = Mediator;
})();