-
Notifications
You must be signed in to change notification settings - Fork 2
/
enQue.js
232 lines (222 loc) · 8.44 KB
/
enQue.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
// enQue class
// Author leathan
// License MIT
'use strict'
// **Constructor** Creates a new enQue object with the specified
// functions. `que = new enQue([fn1, fn2, fn3])`
// You can also create an empty object via `new enQue()`
function enQue(init) {
this.que = [];
if(init) this.add(init);
// __returns itself for use in chaining__
return this;
}
// **Compact** Removes undefined and null values.
// This method should never ever be called
// directly, and it will probably become private
// in the future.. but who knows. `que.compact()`
enQue.prototype.compact = function() {
var i = 0;
const res = [];
for (let val of this.que) if(val) res[i++] = val;
this.que = res;
// __returns itself for use in chaining__
return this;
}
// **enQue.fill** Fills an enQue object with `fn`'s `n` times.
// `que.fill((_,n)=>{console.log('works');n()}, 7)`
// running that que will display 'works' 7 times.
enQue.prototype.fill = function(fn, n) {
for(let i = 0; i < n; i++) {
this.que.push(fn);
}
// __returns itself for use in chaining__
return this;
}
// **enQue.add** Adds `fn` to the enQue object.
// `que.add((_,n,__,i)=>{console.log(i)})` or you can
// specify an array of functions `que.add([fn1, fn1])`
enQue.prototype.add = function(fn) {
if(fn.constructor.name === 'Array') {
for(let i = 0, l = fn.length; i < l; i++) {
this.que.push(fn[i]);
}
} else {
this.que.push(fn);
}
// __returns itself for use in chaining__
return this;
}
// **enQue.clear** Clears all functions from the que
// `que.clear()`
enQue.prototype.clear = function() {
this.que = [];
return this;
// __returns itself for use in chaining__
}
// **enQue.remove** Removes an item from the que.
// `que.remove("(_,n,__,i)=>{console.log(i)}")`
// `que.remove(fn1, 2)` removes 2 occurances of fn1
// `que.remove(7)` removes the 8th function (0 indexed)
// to remove an array of function refs and/or strinfified functions.
// `que.remove([fn1, "(_,n,__,i)=>{console.log(i)}", fn2])`
// `que.remove([fn1, fn2, fn3], 2)` will only remove fn1 and fn2.
enQue.prototype.remove = function(item, amount) {
// Here we extract the items type.
let type = item.constructor.name;
if(type === 'Number') {
return this.que.splice(item, 1);
}
else if(type === "Array") {
amount = amount || Infinity;
let removed = 0;
for(let i=0, l=item.length; i<l; i++) {
var check = item[i].constructor.name === 'Function' ? item[i] : item[i].toString();
for(let j=0, l2=this.que.length; j<l2; j++) {
// Make sure we dont remove more than amount!
if(removed === amount) break;
if(this.que[j] === check) {
delete this.que[j];
removed++;
}
}
}
// __returns itself after compacting for use in chaining__
return this.compact();
}
else {
let check = type === 'Function' ? item : item.toString();
amount = amount || Infinity;
let removed = 0;
for(let i=0, l=this.que.length; i<l; i++) {
// Make sure we dont remove more than amount!
if(removed === amount) break;
if(check === check.constructor.name === 'Function' ? this.que[i] : this.que[i].toString()) {
delete this.que[i]
removed++;
}
}
// __returns itself after compacting for use in chaining__
return this.compact();
}
}
// **executeQue** Executes the que, you should not need to call
// this function directly, for example if `data` doesnt exist
// you will not be able to consume/output properly.
// `run` makes sure data exists. On the offchance you need to
// bypass the promise system its avialable `que.executeQue()`
// but remember to pass in `data` and `done` if needed.
enQue.prototype.executeQue = function(data, done) {
// preserve the original callback for potential que rebuilding.
var orig = done;
// `i` is our iterator, `quit` and `inject` are used to check if we need
// to quit early and resolve the data, or inject `Function`.
var i = 0, quit = false, inject = false, injectFn = false;
// The `nest` function allows us to itterate through the que while constantly
// nesting callbacks.
this.nest((done, next) =>
options => {
// Options can be any operation to perform while nesting callbacks.
// Currently options must be a specific `JSON Object`, or a `Number`, if its JSON
// then it needs a `quit` or `inject` property. Otherwise **0** terminates que,
// exposing the data immitiadtly. A negative Number sends the data
// to backwards in the que (to a new que **techincally**), Positive Numbers
// send the data forward, `i` is current callback index being nested.
if(options === 0) return next = orig;
if(options !== data && options === Object(options)) {
if((!options.function && !options.inject) && !options.quit)
throw new Error(`${options} is not supported, valid fomat could be +n, -n, 0, or, {quit:+n}, {inject:+n,function:Function}`)
else if(options.quit) {
quit = options.quit;
}
else if(options.inject) {
inject = options.inject - 1;
injectFn = options.function;
}
}
else if(options !== data && options) {
// creates a temp que to hold our new que.
let tmpQue = [];
// let `j` be the position we are skipping to.
// lets say `next(-3)` was passed, so `options = -3`.
// `i` is the current que spot - 1 that called `next(-3)`.
// lets say `i` was **5**, so we want to go back to **1**.
// so `j = 5 + -3 - (options < 0) = 1`
// so start at position 1, until the end.
var j = i + options - (options < 0);
if(!this.que[j-1]) throw `${options} out of bounds, no que position ${j} exists.`
for(let l = this.que.length; j < l; j++) {
tmpQue.push(this.que[j])
}
// sets the que to the one we just built.
this.que = tmpQue;
// Apends the original data exposure callback.
this.que.push(orig)
this.executeQue(data);
// Makes sure the current execution goes nowhere.
done = ()=>{}
next = ()=>{}
}
// if quit is specified, checks if we need to quit
// and if so sets `next` to resolve `data`, otherwise de-increments
// the checker variable.
if(quit !== false) {
if(quit === 0) { next = orig; quit = false }
else quit--;
}
// if inject is specified, checks if we need to inject
// the `Function` if so waits it injects the function
// **which is NOT part of the que** and hence its execution is
// not synchronized, this will probably be fixed in the future.
// calls `next`, otherwise de-increments checker and calls `next`.
if(inject !== false) {
if(inject === 0) {
next(data, done, i++, orig);
injectFn(data);
injectFn = inject = false;
} else {
inject--;
next(data, done, i++, orig);
}
} else {
// no special object options were specified just proceeds.
next(data, done, i++, orig)
}
}
// this sets our initial accumulator to the function done each successive call
// then creates another function callback nest where the old `done` becomes the callback
// of the new `done` the original `done` passed in here does nothing more then resolve the
// data. Which is passed in immidiatly since right after nesting the entire composition is executed.
, done)(data);
}
// **enQue.run** Creates a promise which resolves when the que ends.
// `que.run(data)` or for ques that dont consume data `que.run()`
// Since a promise is returns you should then call `.then(data=>{})`
// and `.catch(error=>{})`
enQue.prototype.run = function(data) {
// Allow ques that dont need to consume data.
if(!data) data = {};
return new Promise((resolve, reject) => {
try {
this.executeQue(data, () => resolve(data));
} catch(error) {
reject(error);
}
// __returns a promise which can be used for chaining__
})
}
// ***enQue.nest*** This method should never be called directly
// unless you know what you are doing. `que.nest()`
enQue.prototype.nest = function(callback, orig) {
var que = this.que, pos = que.length - 1, nest;
// Set the initial done to resolve(data).
nest = orig;
for (; pos >= 0; pos--) {
// Now our original done is a callback.
// which keeps being nested till pos == 0.
nest = callback(nest, que[pos]);
}
// __returns the fully nested object__
return nest;
};
module.exports = enQue;