-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtelegraph.js
117 lines (103 loc) · 4.6 KB
/
telegraph.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
/**
* Converts the passed object, or a new object, into an event emitter
* @param {object} obj The object to convert
* @returns {object} The passed object for chaining
*/
telegraph = function (obj) {
'use strict';
obj = obj || {};
// we store the list of handlers as a local variable inside the scope so
// that we don't have to add random properties to the object we are
// wrapping. (prefixing variables in the object with an underscore or two is
// an ugly solution)
var handlers = {};
/**
* Add a listener
* @param {string} eventName The name of the event
* @param {function} handler The handler function for the event
* @param {boolean} front Handler is inserted at the front of the call chain when true
* @returns {object} This object for chaining
*/
obj.on = function (eventName, handler, front) {
// either use the existing array or create a new one for this event
(handlers[eventName] = handlers[eventName] || [])
// add the handler to the array
[front ? 'unshift' : 'push'](handler);
return obj;
};
/**
* Add a listener that will only be called once
* @param {string} eventName The name of the event
* @param {function} handler The handler function for the event
* @param {boolean} front Handler is inserted at the front of the call chain when true
* @returns {object} This object for chaining
*/
obj.once = function (eventName, handler, front) {
// create a wrapper listener, that will remove itself after it is called
function wrappedHandler() {
// remove ourself, and then call the real handler with the args
// passed to this wrapper
handler.apply(obj.off(eventName, wrappedHandler), arguments);
}
// in order to allow that these wrapped handlers can be removed by
// removing the original function, we save a reference to the original
// function
wrappedHandler.h = handler;
// call the regular add listener function with our new wrapper
return obj.on(eventName, wrappedHandler, front);
};
/**
* Remove a listener. Remove all listeners for eventName if handler is
* omitted. Remove all listeners for all event names if eventName is also
* omitted.
* @param {string} eventName The name of the event
* @param {function} handler The handler function for the event
* @returns {object} This object for chaining
*/
obj.off = function (eventName, handler) {
// if no eventName, clear all event handlers for all events
if (eventName === undefined) {
handlers = {};
return obj;
} // if
// loop through all handlers for this eventName to see if the handler
// passed in was any of them so we can remove it
// if no handler, clear all handlers for the event instead
var list = handler ? handlers[eventName] || [] : [];
for (var i = 0; i < list.length; i++) {
// either this item is the handler passed in, or this item is a
// wrapper for the handler passed in. See the 'once' function
if (list[i] === handler || list[i].h === handler)
list.splice(i--, 1);
} // for( i )
// cleanup if no events for the eventName
if (!list.length) {
// remove the array for this eventname (if it doesn't exist then
// this isn't really hurting anything)
delete handlers[eventName];
} // if
return obj;
};
/**
* Dispatches the named event, calling all registered handler functions. If
* any handler returns false, the event subsequent handlers are not called
* and false is returned; Otherwise, all handlers are called and true is
* returned.
* @param {string} eventName The name of the event to dispatch
* @returns {boolean} False if any handler returns false, true otherwise.
*/
obj.emit = function (eventName) {
// loop through all handlers for this event name and call them all
// arguments is "array-like", so call slice() from list instead
// handlers can return false to cancel event
// copy list in case on()/off() are called during emit
var list = handlers[eventName] && handlers[eventName].slice() || [];
var args = list.slice.call(arguments, 1);
for (var i = 0; i < list.length; ++i) {
if (list[i].apply(obj, args) === false)
return false;
} // for( i )
return true;
};
return obj;
}; // telegraph( )