Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // Disclaimer: Some of the functions in this module implement APIs from
6 // Jeremy Ashkenas's http://underscorejs.org/ library and all credits for
7 // those goes to him.
9 "use strict";
11 module.metadata = {
12 "stability": "unstable"
13 };
15 const { deprecateFunction } = require("../util/deprecate");
16 const { setImmediate, setTimeout, clearTimeout } = require("../timers");
18 const arity = f => f.arity || f.length;
20 const name = f => f.displayName || f.name;
22 const derive = (f, source) => {
23 f.displayName = name(source);
24 f.arity = arity(source);
25 return f;
26 };
28 /**
29 * Takes variadic numeber of functions and returns composed one.
30 * Returned function pushes `this` pseudo-variable to the head
31 * of the passed arguments and invokes all the functions from
32 * left to right passing same arguments to them. Composite function
33 * returns return value of the right most funciton.
34 */
35 const method = (...lambdas) => {
36 return function method(...args) {
37 args.unshift(this);
38 return lambdas.reduce((_, lambda) => lambda.apply(this, args),
39 void(0));
40 };
41 };
42 exports.method = method;
44 /**
45 * Takes a function and returns a wrapped one instead, calling which will call
46 * original function in the next turn of event loop. This is basically utility
47 * to do `setImmediate(function() { ... })`, with a difference that returned
48 * function is reused, instead of creating a new one each time. This also allows
49 * to use this functions as event listeners.
50 */
51 const defer = f => derive(function(...args) {
52 setImmediate(invoke, f, args, this);
53 }, f);
54 exports.defer = defer;
55 // Exporting `remit` alias as `defer` may conflict with promises.
56 exports.remit = defer;
58 /**
59 * Invokes `callee` by passing `params` as an arguments and `self` as `this`
60 * pseudo-variable. Returns value that is returned by a callee.
61 * @param {Function} callee
62 * Function to invoke.
63 * @param {Array} params
64 * Arguments to invoke function with.
65 * @param {Object} self
66 * Object to be passed as a `this` pseudo variable.
67 */
68 const invoke = (callee, params, self) => callee.apply(self, params);
69 exports.invoke = invoke;
71 /**
72 * Takes a function and bind values to one or more arguments, returning a new
73 * function of smaller arity.
74 *
75 * @param {Function} fn
76 * The function to partial
77 *
78 * @returns The new function with binded values
79 */
80 const partial = (f, ...curried) => {
81 if (typeof(f) !== "function")
82 throw new TypeError(String(f) + " is not a function");
84 let fn = derive(function(...args) {
85 return f.apply(this, curried.concat(args));
86 }, f);
87 fn.arity = arity(f) - curried.length;
88 return fn;
89 };
90 exports.partial = partial;
92 /**
93 * Returns function with implicit currying, which will continue currying until
94 * expected number of argument is collected. Expected number of arguments is
95 * determined by `fn.length`. Using this with variadic functions is stupid,
96 * so don't do it.
97 *
98 * @examples
99 *
100 * var sum = curry(function(a, b) {
101 * return a + b
102 * })
103 * console.log(sum(2, 2)) // 4
104 * console.log(sum(2)(4)) // 6
105 */
106 const curry = new function() {
107 const currier = (fn, arity, params) => {
108 // Function either continues to curry arguments or executes function
109 // if desired arguments have being collected.
110 const curried = function(...input) {
111 // Prepend all curried arguments to the given arguments.
112 if (params) input.unshift.apply(input, params);
113 // If expected number of arguments has being collected invoke fn,
114 // othrewise return curried version Otherwise continue curried.
115 return (input.length >= arity) ? fn.apply(this, input) :
116 currier(fn, arity, input);
117 };
118 curried.arity = arity - (params ? params.length : 0);
120 return curried;
121 };
123 return fn => currier(fn, arity(fn));
124 };
125 exports.curry = curry;
127 /**
128 * Returns the composition of a list of functions, where each function consumes
129 * the return value of the function that follows. In math terms, composing the
130 * functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
131 * @example
132 *
133 * var greet = function(name) { return "hi: " + name; };
134 * var exclaim = function(statement) { return statement + "!"; };
135 * var welcome = compose(exclaim, greet);
136 *
137 * welcome('moe'); // => 'hi: moe!'
138 */
139 function compose(...lambdas) {
140 return function composed(...args) {
141 let index = lambdas.length;
142 while (0 <= --index)
143 args = [lambdas[index].apply(this, args)];
145 return args[0];
146 };
147 }
148 exports.compose = compose;
150 /*
151 * Returns the first function passed as an argument to the second,
152 * allowing you to adjust arguments, run code before and after, and
153 * conditionally execute the original function.
154 * @example
155 *
156 * var hello = function(name) { return "hello: " + name; };
157 * hello = wrap(hello, function(f) {
158 * return "before, " + f("moe") + ", after";
159 * });
160 *
161 * hello(); // => 'before, hello: moe, after'
162 */
163 const wrap = (f, wrapper) => derive(function wrapped(...args) {
164 return wrapper.apply(this, [f].concat(args));
165 }, f);
166 exports.wrap = wrap;
168 /**
169 * Returns the same value that is used as the argument. In math: f(x) = x
170 */
171 const identity = value => value;
172 exports.identity = identity;
174 /**
175 * Memoizes a given function by caching the computed result. Useful for
176 * speeding up slow-running computations. If passed an optional hashFunction,
177 * it will be used to compute the hash key for storing the result, based on
178 * the arguments to the original function. The default hashFunction just uses
179 * the first argument to the memoized function as the key.
180 */
181 const memoize = (f, hasher) => {
182 let memo = Object.create(null);
183 let cache = new WeakMap();
184 hasher = hasher || identity;
185 return derive(function memoizer(...args) {
186 const key = hasher.apply(this, args);
187 const type = typeof(key);
188 if (key && (type === "object" || type === "function")) {
189 if (!cache.has(key))
190 cache.set(key, f.apply(this, args));
191 return cache.get(key);
192 }
193 else {
194 if (!(key in memo))
195 memo[key] = f.apply(this, args);
196 return memo[key];
197 }
198 }, f);
199 };
200 exports.memoize = memoize;
202 /**
203 * Much like setTimeout, invokes function after wait milliseconds. If you pass
204 * the optional arguments, they will be forwarded on to the function when it is
205 * invoked.
206 */
207 const delay = function delay(f, ms, ...args) {
208 setTimeout(() => f.apply(this, args), ms);
209 };
210 exports.delay = delay;
212 /**
213 * Creates a version of the function that can only be called one time. Repeated
214 * calls to the modified function will have no effect, returning the value from
215 * the original call. Useful for initialization functions, instead of having to
216 * set a boolean flag and then check it later.
217 */
218 const once = f => {
219 let ran = false, cache;
220 return derive(function(...args) {
221 return ran ? cache : (ran = true, cache = f.apply(this, args));
222 }, f);
223 };
224 exports.once = once;
225 // export cache as once will may be conflicting with event once a lot.
226 exports.cache = once;
228 // Takes a `f` function and returns a function that takes the same
229 // arguments as `f`, has the same effects, if any, and returns the
230 // opposite truth value.
231 const complement = f => derive(function(...args) {
232 return args.length < arity(f) ? complement(partial(f, ...args)) :
233 !f.apply(this, args);
234 }, f);
235 exports.complement = complement;
237 // Constructs function that returns `x` no matter what is it
238 // invoked with.
239 const constant = x => _ => x;
240 exports.constant = constant;
242 // Takes `p` predicate, `consequent` function and an optional
243 // `alternate` function and composes function that returns
244 // application of arguments over `consequent` if application over
245 // `p` is `true` otherwise returns application over `alternate`.
246 // If `alternate` is not a function returns `undefined`.
247 const when = (p, consequent, alternate) => {
248 if (typeof(alternate) !== "function" && alternate !== void(0))
249 throw TypeError("alternate must be a function");
250 if (typeof(consequent) !== "function")
251 throw TypeError("consequent must be a function");
253 return function(...args) {
254 return p.apply(this, args) ?
255 consequent.apply(this, args) :
256 alternate && alternate.apply(this, args);
257 };
258 };
259 exports.when = when;
261 // Apply function that behaves as `apply` does in lisp:
262 // apply(f, x, [y, z]) => f.apply(f, [x, y, z])
263 // apply(f, x) => f.apply(f, [x])
264 const apply = (f, ...rest) => f.apply(f, rest.concat(rest.pop()));
265 exports.apply = apply;
267 // Returns function identical to given `f` but with flipped order
268 // of arguments.
269 const flip = f => derive(function(...args) {
270 return f.apply(this, args.reverse());
271 }, f);
272 exports.flip = flip;
275 // Takes field `name` and `target` and returns value of that field.
276 // If `target` is `null` or `undefined` it would be returned back
277 // instead of attempt to access it's field. Function is implicitly
278 // curried, this allows accessor function generation by calling it
279 // with only `name` argument.
280 const field = curry((name, target) =>
281 // Note: Permisive `==` is intentional.
282 target == null ? target : target[name]);
283 exports.field = field;
285 // Takes `.` delimited string representing `path` to a nested field
286 // and a `target` to get it from. For convinience function is
287 // implicitly curried, there for accessors can be created by invoking
288 // it with just a `path` argument.
289 const query = curry((path, target) => {
290 const names = path.split(".");
291 const count = names.length;
292 let index = 0;
293 let result = target;
294 // Note: Permisive `!=` is intentional.
295 while (result != null && index < count) {
296 result = result[names[index]];
297 index = index + 1;
298 }
299 return result;
300 });
301 exports.query = query;
303 // Takes `Type` (constructor function) and a `value` and returns
304 // `true` if `value` is instance of the given `Type`. Function is
305 // implicitly curried this allows predicate generation by calling
306 // function with just first argument.
307 const isInstance = curry((Type, value) => value instanceof Type);
308 exports.isInstance = isInstance;
310 /*
311 * Takes a funtion and returns a wrapped function that returns `this`
312 */
313 const chainable = f => derive(function(...args) {
314 f.apply(this, args);
315 return this;
316 }, f);
317 exports.chainable = chainable;
318 exports.chain =
319 deprecateFunction(chainable, "Function `chain` was renamed to `chainable`");
321 // Functions takes `expected` and `actual` values and returns `true` if
322 // `expected === actual`. Returns curried function if called with less then
323 // two arguments.
324 //
325 // [ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
326 const is = curry((expected, actual) => actual === expected);
327 exports.is = is;
329 const isnt = complement(is);
330 exports.isnt = isnt;
332 /**
333 * From underscore's `_.debounce`
334 * http://underscorejs.org
335 * (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
336 * Underscore may be freely distributed under the MIT license.
337 */
338 const debounce = function debounce (fn, wait) {
339 let timeout, args, context, timestamp, result;
341 let later = function () {
342 let last = Date.now() - timestamp;
343 if (last < wait) {
344 timeout = setTimeout(later, wait - last);
345 } else {
346 timeout = null;
347 result = fn.apply(context, args);
348 context = args = null;
349 }
350 };
352 return function (...aArgs) {
353 context = this;
354 args = aArgs;
355 timestamp = Date.now();
356 if (!timeout) {
357 timeout = setTimeout(later, wait);
358 }
360 return result;
361 };
362 };
363 exports.debounce = debounce;
365 /**
366 * From underscore's `_.throttle`
367 * http://underscorejs.org
368 * (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
369 * Underscore may be freely distributed under the MIT license.
370 */
371 const throttle = function throttle (func, wait, options) {
372 let context, args, result;
373 let timeout = null;
374 let previous = 0;
375 options || (options = {});
376 let later = function() {
377 previous = options.leading === false ? 0 : Date.now();
378 timeout = null;
379 result = func.apply(context, args);
380 context = args = null;
381 };
382 return function() {
383 let now = Date.now();
384 if (!previous && options.leading === false) previous = now;
385 let remaining = wait - (now - previous);
386 context = this;
387 args = arguments;
388 if (remaining <= 0) {
389 clearTimeout(timeout);
390 timeout = null;
391 previous = now;
392 result = func.apply(context, args);
393 context = args = null;
394 } else if (!timeout && options.trailing !== false) {
395 timeout = setTimeout(later, remaining);
396 }
397 return result;
398 };
399 };
400 exports.throttle = throttle;