-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjsCallbacksCollectionManager.js
More file actions
118 lines (109 loc) · 7.4 KB
/
jsCallbacksCollectionManager.js
File metadata and controls
118 lines (109 loc) · 7.4 KB
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
/*
Checks if a value is undefined in a way that will work in all browsers as suggested here:
http://stackoverflow.com/questions/7041123/test-if-something-is-not-undefined-in-javascript/17635768#17635768
Returns true if undefined and returns false if not undefined
*/
function checkIfUndefined(value) {
try {
if (typeof (value) !== 'undefined') {
return false;
}
else {
return true;
}
}
catch (e) {
return true;
}
}
/*
Manages callbacks dictionary with ids to different callbacks allowing to override specific previous callbacks.
Useful for example when there is a global callbacks array which different parts of the code register callbacks to and you want different parts of code not
to override each others callbacks however to let the specific part itself override its own callback for example on window resize event.
New callback is identified by the uniqueCallbacksId and if the previous uniqueCallbacksId is same as new uniqueCallbacksId then the callback is overridden.
Make sure the _callbacksIds and _callbacksList properties of the callbacks dictionary are not used outside this because they are changed here.
Assumptions: ECMA 5+ browsers or appropriate shim for Object.keys.
Parameters:
existingCallbacksDictionary - An empty object {} or a dictionary created by a previous call to addCallbacksToDictionary on the empty object or on the output of addCallbacksToDictionary.
newCallbacks - New collection of callbacks to merge into existingCallbacksDictionary.
uniqueCallbacksId - Unique id associated with current new collection of callbacks. Pass same uniqueCallbacksId to override previous callbacks associated with that id.
Example:
var a = {};
addCallbacksToDictionary(a, { ona: function(){alert(1);}}, "someId");
a.ona(); // alerts 1
addCallbacksToDictionary(a, { ona: function(){alert(2);}}, "Anotherid");
a.ona(); // alerts 1 and then alerts 2
addCallbacksToDictionary(a, { ona: function(){alert(3);}}, "someId");
a.ona(); // alerts 3 and then alerts 2
addCallbacksToDictionary(a, { ona: function(){alert(7);}}, "thirdId");
a.ona(); // alerts 3 and then alerts 2 and then 7
addCallbacksToDictionary(a, { ona: function(){alert(6);}}, "Anotherid");
a.ona(); // alerts 3 and then alerts 6 and then 7
*/
function addCallbacksToDictionary(existingCallbacksDictionary, newCallbacks, uniqueCallbacksId) {
/* Check if object is empty according to: http://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object/32108184#32108184 */
if (Object.keys(existingCallbacksDictionary).length === 0 && existingCallbacksDictionary.constructor === Object) {
/* Don't just assign but make a clone to prevent circular reference in the callbacksList */
/* Most efficient looping as stated here: http://stackoverflow.com/questions/5349425/whats-the-fastest-way-to-loop-through-an-array-in-javascript/7252102#7252102 */
for (let currCallbackIndex = 0, eventsName = Object.keys(newCallbacks), callbackLength = eventsName.length; currCallbackIndex < callbackLength; ++currCallbackIndex) {
const currCallbackName = eventsName[currCallbackIndex];
existingCallbacksDictionary[currCallbackName] = newCallbacks[currCallbackName];
}
existingCallbacksDictionary._callbacksIds = {};
existingCallbacksDictionary._callbacksIds[uniqueCallbacksId] = 0;
existingCallbacksDictionary._callbacksList = [newCallbacks];
}
else {
if (!checkIfUndefined(existingCallbacksDictionary._callbacksIds[uniqueCallbacksId])) {
/* Override previous callbacks which had same id and leave new callbacks at same priority as previous */
existingCallbacksDictionary._callbacksList[existingCallbacksDictionary._callbacksIds[uniqueCallbacksId]] = newCallbacks;
}
else {
existingCallbacksDictionary._callbacksList.push(newCallbacks);
existingCallbacksDictionary._callbacksIds[uniqueCallbacksId] = existingCallbacksDictionary._callbacksList.length - 1;
}
/* Build final callbacks from the callbacks callbacksList using appropriate priorities */
for (let currCallbackIndex = 0, eventsName = Object.keys(existingCallbacksDictionary), callbackLength = eventsName.length; currCallbackIndex < callbackLength; ++currCallbackIndex) {
const currCallbackName = eventsName[currCallbackIndex];
/* Delete functions from existing callbacks dictionary, then add later in wrapper */
if (currCallbackName !== "_callbacksIds" && currCallbackName !== "_callbacksList" && existingCallbacksDictionary[currCallbackName].constructor === Function) {
delete existingCallbacksDictionary[currCallbackName];
}
}
/* First calculate list of callbacks for the events and then create one callback function calling them in desired order. This is better than overloading the stack by each function calling the next function. */
var eventsCallbacksLists = {};
for (var currPriority = 0, maxPriority = existingCallbacksDictionary._callbacksList.length; currPriority < maxPriority; currPriority++) {
var currCallbacksDictionary = existingCallbacksDictionary._callbacksList[currPriority];
for (let currCallbackIndex = 0, eventsName = Object.keys(currCallbacksDictionary), callbackLength = eventsName.length; currCallbackIndex < callbackLength; ++currCallbackIndex) {
const currCallbackName = eventsName[currCallbackIndex];
var currCallbackValue = currCallbacksDictionary[currCallbackName];
/* Add only functions, we don't want add properties in wrapper function */
if (currCallbackValue.constructor === Function) {
/* Add callback to appropriate list */
if (eventsCallbacksLists[currCallbackName]) {
eventsCallbacksLists[currCallbackName].push(currCallbackValue);
}
else {
eventsCallbacksLists[currCallbackName] = [currCallbackValue];
}
}
}
}
/* Function that wrap all functions by key in one function callback and in cycle call all of them */
var combineAllSimilarFunctionsByCallbackNameIntoOne = function (currCallbackName, currClosureCallbacksList) {
/* Call all callbacks in the list */
existingCallbacksDictionary[currCallbackName] = function () {
for (var i = 0, callbackListLength = currClosureCallbacksList.length; i < callbackListLength; ++i) {
currClosureCallbacksList[i]();
}
};
};
for (let currCallbackIndex = 0, eventsName = Object.keys(eventsCallbacksLists), callbackLength = eventsName.length; currCallbackIndex < callbackLength; ++currCallbackIndex) {
const currCallbackName = eventsName[currCallbackIndex];
/* Save in closure to prevent it changing in loop */
var currClosureCallbacksList = eventsCallbacksLists[currCallbackName];
/* It must be a function call, else in anonymous function we receive last value of variable currClosureCallbacksList */
combineAllSimilarFunctionsByCallbackNameIntoOne(currCallbackName, currClosureCallbacksList);
}
}
}