addon-sdk/source/lib/sdk/lang/functional.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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;

mercurial