Coverage

97.0%
165
160
5

LineHitsSource

/home/luis/noboxout/js-function-enhancements/index.js

100.0%
3
3
0
LineHitsSource
11(function () {
21 "use strict";
3
41 module.exports = require("/home/luis/noboxout/js-function-enhancements/./lib/functions.js");
5
6}());

/home/luis/noboxout/js-function-enhancements/lib/functions.js

96.9%
162
157
5
LineHitsSource
11(function () {
21 "use strict";
3
4 // http://jsperf.com/apply-usages
5
61 var slice = Array.prototype.slice,
7 __pass,
8 __debounce,
9 __every,
10 __funnel;
11
12 //
13 // Function
14 //
15
16 /**
17 * Returns a closure with arguments and bind
18 *
19 * credits - mootools
20 *
21 * @note: If you want to send null as arguments use: .pass([null], ?)
22 * @param {Mixed} bind Changes the scope of this within the returned function
23 * @param {Array} args The arguments passed
24 * @return {Function} closure
25 */
261 module.exports.pass = __pass = function (fn, args, bind) {
2738 if (args !== null && args !== undefined) {
2837 args = slice.call(args);
29 }
30
3138 return function () {
3235 var fargs = args || arguments,
33 fbind = bind || fn;
34
3535 if (fargs.length) {
365 return fn.apply(fbind, fargs);
37 }
3830 return fn.call(fbind);
39 };
40 };
41
42 /**
43 * Returns a closure with the given arguments before the ones you send at the call
44 *
45 * @param {Mixed} bind Changes the scope of this within the returned function
46 * @param {Array} args The arguments passed
47 * @return {Function} closure
48 */
491 module.exports.prepend = function (fn, args, bind) {
502 var max = args.length;
51
522 args = args ? slice.call(args) : [];
53
542 return function () {
552 var fbind = bind || fn,
56 cloned = [],
57 i;
58
592 for (i = 0; i < max; ++i) {
604 cloned.push(args[i]);
61 }
622 for (i = 0; i < arguments.length; ++i) {
631 cloned.push(arguments[i]);
64 }
65
662 return fn.apply(fbind, cloned);
67 };
68 };
69
70 /**
71 * Returns a closure with the given arguments after the ones you send at the call
72 *
73 * @param {Mixed} bind Changes the scope of this within the returned function
74 * @param {Array} args The arguments passed
75 * @return {Function} closure
76 */
771 module.exports.append = function (fn, args, bind) {
784 var max = args.length;
79
804 args = args ? slice.call(args) : [];
81
824 return function () {
8311 var fbind = bind || fn,
84 cloned = [],
85 i;
86
8711 for (i = 0; i < arguments.length; ++i) {
881 cloned.push(arguments[i]);
89 }
9011 for (i = 0; i < max; ++i) {
9113 cloned.push(args[i]);
92 }
93
9411 return fn.apply(fbind, cloned);
95 };
96 };
97
98 /**
99 * Delays the execution of a function by a specified duration.
100 *
101 * credits - mootools
102 *
103 * @note use: clearTimeout to stop the scheduled execution
104 * @param {Number} delay_ms
105 * @param {Mixed} bind Changes the scope of this within the returned function
106 * @param {Array} args The arguments passed
107 * @return {Number} the interval so you can clearTimeout
108 */
1091 module.exports.delay = function (fn, delay_ms, bind, args) {
1100 return setTimeout(__pass(fn, args, bind || fn), delay_ms);
111 };
112
113 /**
114 * Executes a function in the specified intervals of time.
115 * Periodic execution can be stopped using the clearInterval function.
116 *
117 * credits - mootools (
118 *
119 * @param {Number} periodical_ms
120 * @param {Mixed} bind Changes the scope of this within the returned function
121 * @param {Array} args The arguments passed
122 * @return {Number} the interval so you can clearInterval
123 */
1241 module.exports.periodical = function (fn, periodical_ms, bind, args) {
1251 return setInterval(__pass(fn, args, bind || fn), periodical_ms);
126 };
127
128 /**
129 * Returns a function, that, as long as it continues to be invoked, will not
130 * be triggered. The function will be called after it stops being called for
131 * N milliseconds.
132 *
133 * credits to underscore
134 *
135 * @param {Number} wait_ms
136 * @param {Mixed} bind Changes the scope of this within the returned function
137 * @param {Array} args The arguments passed
138 */
1391 module.exports.debounce = __debounce = function (fn, wait_ms, args, bind) {
1402 var timeout;
141
1422 return function () {
14335 var later = __pass(fn, args || arguments, bind || fn);
144
14535 clearTimeout(timeout);
14635 timeout = setTimeout(later, wait_ms);
147 };
148 };
149
150 /**
151 * Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with.
152 *
153 * credits to underscore
154 *
155 * @param {Number} wait_ms
156 * @param {Mixed} bind Changes the scope of this within the returned function
157 * @param {Array} args The arguments passed
158 */
1591 module.exports.throttle = function (fn, wait_ms, bind, args) {
1601 var timeout,
161 throttling,
162 more,
1631 whenDone = __debounce(function () { more = throttling = false; }, wait_ms);
164
1651 return function () {
16629 var fargs = args || arguments,
167 fbind = bind || fn,
168 later = function () {
1692 timeout = null;
170
1712 if (more) {
1722 fn.apply(fbind, fargs);
173 }
174
1752 whenDone();
176 };
177
17829 if (!timeout) {
1792 timeout = setTimeout(later, wait_ms);
180 }
181
18229 if (throttling) {
18328 more = true;
184 } else {
1851 fn.apply(fbind, fargs);
186 }
187
18829 whenDone();
18929 throttling = true;
190 };
191 };
192
193 /**
194 * Returns a function that will be executed at most one time, no matter how
195 * often you call it. Useful for lazy initialization.
196 *
197 * @param {Mixed} bind Changes the scope of this within the returned function
198 * @param {Array} args The arguments passed
199 * @returns the value of the first execution
200 */
2011 module.exports.once = function (fn, bind, args) {
2021 var first = false,
203 memo;
204
2051 return function () {
2062 var fargs = args || arguments,
207 fbind = bind || fn;
208
2092 if (first) {
2101 return memo;
211 }
212
2131 first = true;
2141 return (memo = fn.apply(fbind, fargs));
215 };
216 };
217
218 /**
219 * Returns a function that will be executed every <ntimes> times at most of <max_executions>
220 *
221 * @param {Number} ntimes every n times
222 * @param {Number} max_executions maximum number of executions
223 * @param {Mixed} bind Changes the scope of this within the returned function
224 * @param {Array} args The arguments passed
225 * @returns the value returned by given function or undefined if it's not executed.
226 */
2271 module.exports.every = __every = function (fn, ntimes, max_executions, bind, args) {
2283 var attempts = 0,
229 calls = 0;
230
2313 return function () {
23226 var fargs = args || arguments,
233 fbind = bind || fn;
234
23526 if (++attempts < ntimes || calls >= max_executions) {
23617 return;
237 }
2389 attempts = 0;
2399 ++calls;
240
2419 return fn.apply(fbind, fargs);
242 };
243 };
244
245 /**
246 * Returns a function that will be executed after being called n times
247 *
248 * @param {Number} ntimes
249 */
2501 module.exports.after = function (fn, ntimes, bind, args) {
2511 return __every(fn, ntimes, 1, bind, args);
252 };
253
254 /**
255 * Returns a function that will be executed ntimes with a given delay between them
256 * If delay is false is cero will be executed right now
257 * If first_delay is false is cero will be executed right now
258 *
259 * @note for delay setInterval is used to be sure that each function execution has the given delay
260 *
261 * @param {Number} ntimes how many times will be executed
262 * @param {Number} delay delay between first and the next execution
263 * @param {Number} first_delay delay between the call and first execution
264 * @param {Function} last_func function to call when all is done with one arguments, an array of the returned
265 * @param {Mixed} bind Changes the scope of this within the returned function
266 * @param {Array} args The arguments passed
267 */
2681 module.exports.nth = function (fn, ntimes, delay, first_delay, last_func, bind, args) {
2693 var outputs = [],
270 interval;
271
2723 delay = delay === undefined ? false : delay;
2733 first_delay = first_delay === undefined ? false : first_delay;
274
275
2763 return function () {
2773 var times = 1,
278 fargs = args || arguments,
279 fbind = bind || fn;
280
281 // allow 0
2823 if (first_delay === false) {
2832 outputs.push(fn.apply(fbind, fargs));
284
2852 if (delay === false) {
2861 for (times = 1; times < ntimes; ++times) {
2874 outputs.push(fn.apply(fbind, fargs));
288 }
289
2901 last_func && last_func(outputs);
291 } else {
2921 interval = setInterval(function () {
2934 outputs.push(fn.apply(fbind, fargs));
2944 if (++times === ntimes) {
2951 clearInterval(interval);
296
2971 last_func && last_func(outputs);
298 }
299 }, delay);
300 }
301
302 } else {
3031 setTimeout(function () {
3041 outputs.push(fn.apply(fbind, fargs));
305
3061 if (delay === false) {
3070 for (times = 1; times < ntimes; ++times) {
3080 outputs.push(fn.apply(fbind, fargs));
309 }
3100 last_func && last_func(outputs);
311 } else {
3121 interval = setInterval(function () {
3134 outputs.push(fn.apply(fbind, fargs));
3144 if (++times === ntimes) {
3151 clearInterval(interval);
316
3171 last_func && last_func(outputs);
318 }
319 }, delay);
320 }
321 }, first_delay);
322 }
323 };
324 };
325
326 /**
327 * Create a function, when called invoke this.
328 * If you called again and it's in execution, the execution is queued. So only (max) execution at time
329 * A new argument is sent to your function, a callback no notify the execution ended
330 *
331 * @param {Number} max How many execution are allowed in parallel
332 * @param {Mixed} bind Changes the scope of this within the returned function
333 * @param {Array} args The arguments passed
334 * @param {String} where append / prepend, the new callback argument
335 */
3361 module.exports.funnel = __funnel = function (fn, max, bind, args, where) {
3372 where = where || "append";
338
3392 var in_execution = 0,
340 fifo = [],
341 self,
342 check = function () {
3439 var el;
3449 if (fifo.length === 0) {
3454 --in_execution;
346 } else {
3475 el = fifo.shift();
3485 self.apply(el.bind, el.args);
349 }
350 };
351
3522 self = module.exports[where](fn, [check]);
353
3542 return function () {
3559 var fargs = args || (arguments.length ? slice.call(arguments) : []),
356 fbind = bind || fn;
357
3589 if (in_execution === max) {
3595 fifo.push({
360 args: fargs,
361 bind: fbind
362 });
3635 return;
364 }
365
3664 ++in_execution;
3674 self.apply(fbind, fargs);
368
369 };
370 };
371
372
373 /**
374 * Create a function that can only be call once in parallel, the followings will be queued
375 * A new argument is sent to your function, a callback no notify the execution ended
376 *
377 * @param {Mixed} bind Changes the scope of this within the returned function
378 * @param {Array} args The arguments passed
379 * @param {String} where append or prepend, where the callback will be.
380 */
3811 module.exports.single = function (fn, bind, args, where) {
3821 return __funnel(fn, 1, bind, args, where);
383 };
384
385 // from: http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
3861 function hash(args) {
38732 var str = JSON.stringify(args),
388 ihash = 0,
389 chc,
390 i;
391
39232 if (str.length === 0) {
3930 return ihash;
394 }
395
39632 for (i = 0; i < str.length; i++) {
397240 chc = str.charCodeAt(i);
398240 ihash = ((ihash << 5) - ihash) + chc;
399240 ihash = ihash & ihash; // Convert to 32bit integer
400 }
401
40232 return ihash;
403 }
404
405 //Function.prototype.cache
406 /**
407 * Creates a function that memoizes the result of func for a given time.
408 * If hash_function is provided it will be used to determine the cache key for storing the result based on the arguments provided to the memoized function.
409 *
410 * @param {Number} cache_time_ms Cache expiration microseconds, -1 to infinite
411 * @param {Mixed} bind The arguments passed
412 * @param {function} hash_function function used to hash the arguments, most simple one: JSON.stringify
413 */
4141 module.exports.cache = function (fn, cache_time_ms, bind, hash_function) {
4151 var cache = {};
416
4171 hash_function = hash_function || hash;
418
4191 return function () {
42032 var args = [],
421 hash_code,
422 ts = cache_time_ms === -1 ? 0 : +(new Date()),
423 fbind = bind || fn,
424 i,
425 max = arguments.length;
426
42732 if (max) {
42816 for (i = 0, max = arguments.length; i < max; ++i) {
42916 args.push(arguments[i]);
430 }
431
432 }
433
43432 hash_code = hash_function(args);
435
43632 if (cache[hash_code] !== undefined) {
43730 if (cache[hash_code].expire === -1 || cache[hash_code].expire > ts) {
43828 return cache[hash_code].ret;
439 } else {
4402 delete cache[hash_code];
441 }
442 }
443
4444 cache[hash_code] = {
445 ret: fn.apply(fbind, args),
446 expire: ts + cache_time_ms
447 };
448
4494 return cache[hash_code];
450
451 };
452 };
453
454 /**
455 * Returns a function that is the composition of a list of functions, each
456 * consuming the return value of the function that follows.
457 *
458 * credits - almost underscore
459 *
460 * @param {Function} any number of functions
461 * @returns {Array} all returned values
462 */
4631 module.exports.compose = function () {
4641 var funcs = arguments;
465
4661 return function () {
4671 var i,
468 ret = [];
469
4701 for (i = 0; i < funcs.length; ++i) {
4713 if (ret.length === 0) {
4721 ret.push(funcs[i]());
473 } else {
4742 ret.push(funcs[i](ret[ret.length - 1]));
475 }
476 }
477
4781 return ret;
479 };
480 };
481
482 /**
483 * Returns a function that is the composition of a list of functions,
484 * returns an array with all returned values by each function
485 *
486 * @param {Function} any number of functions
487 * @returns {Array} all returned values
488 */
4891 module.exports.sequencial = function () {
4901 var funcs = arguments;
491
4921 return function () {
4931 var i,
494 ret = [];
495
4961 for (i = 0; i < funcs.length; ++i) {
4973 ret.push(funcs[i]());
498 }
499
5001 return ret;
501 };
502 };
503
504}());