<commit_before>//    Lemonad.js
//    http://www.functionaljs.org
//    (c) 2013 Fogus, Ariadne Softworks
//    Lemonad may be freely distributed under the MIT license

// *Always start with the secret JavaScript sauce...*
;(function() {
  // Dependencies
  // ------------

  // I want to require Underscore.js if it's not already available.
  var _ = this._ || require('underscore-contrib');

  // Setup
  // -----

  // Establish the root object, `window` in the browser, or `global` on the server.

  var root = this;

  // The namespace object `L` is a function in its own right that returns
  // a partially applied function of an arbitrary number of arguments.
  // For example, you can use `L` to partially apply the first argument to the
  // `_.template` function:
  //
  //     var hi = L(_.template, "Hello <%= name %>");
  //     _.map([{name: 'Joe'}, {name: 'Bob'}, {name: 'Cleveland'}], hi);
  //
  //     //=> ["Hello Joe", "Hello Bob", "Hello Cleveland"]
  //
  // *signature:* `(any* -> a) -> any* -> (any* -> a)`

  var L = function(fun /*, args */) {
    var args = _.rest(arguments);

    if (_.some(args)) {
      // Default to the native `bind` if available
      if (_.exists(fun.bind)) {
        return fun.bind.apply(fun, _.cons(undefined, args));
      }

      return function(/* arguments */) {
        return fun.apply(null, args.concat(_.toArray(arguments)));
      };
    }
    else return L.curry(fun);
  };

  // Current version
  L.VERSION = '0.6.3';

  // Internal utilities
  // ------------------

  // A simple function to throw an `Error`
  function fail(str) {
    throw new Error(str);
  }

  // A temporary way to derive the name of a function. I hope to
  // eliminate the need sooner rather than later.
  //
  // *signature:* `(a -> b) -> string`

  function nom(fun) {
    var src = fun.toString();
    src = src.substr('function '.length);
    var n = src.substr(0, src.indexOf('('));

    if (!_.isFunction(fun)) fail("Cannot derive the name of a non-function.");
    if (fun.name && (fun.name === n)) return fun.name;
    if (n) return n;
    fail("Anonymous function has no name.");
  }

  // Delegates down to the snapshot method if an object has one
  // or clones the object outright if not.  This is not as robust
  // as it could be.  Explore the possibility of pulling in
  // Lo-Dash's impl.

  var snapshot = function(thing) {
    if (_.exists(thing) && _.exists(thing.snapshot))
      return thing.snapshot();
    else
      return _.snapshot(thing);
  };

  // Useful tools
  // ------------

  // Used as a unique value to signify certain conditions in Lemonad.
  // Also takes a number of args and returns an array of unique values.
  //
  // *signature:* `any* -> [any*]`

  L.$ = function(){
    return _.uniq(arguments);
  };

  // A function to get at an index into an array
  L.nth = function(array, index) {
    if ((index < 0) || (index > array.length - 1)) throw Error("Attempting to index outside the bounds of the array.");

    return array[index];
  }

  // increment, decrement and halve
  //
  // *signature:* `number -> number`
  L.inc = function(n) { return n+1; };
  L.dec = function(n) { return n-1; };
  L.halve = function(n) { return n/2; };

  // Returns the type constructor
  //
  // *signature:* `{any: any} -> (any* -> any)`
  L.ctor = function(obj) {
    return _.exists(obj) ? obj.constructor : null;
  };

  // Useful predicates
  // -----------------

  // ### Type predicates

  // A reference is something that derives from a Lemonad Hole object
  //
  // *signature:* `a -> boolean`
  L.isReference = function(x) {
    return (x instanceof L.Hole) || (x instanceof L.CAS);
  };

  // Returns a function that will invoke the given method
  // on any object that it receives as an argument.
  L.meth = L.walterWhite = L.invoker = function(method /*, args*/) {
    var pargs = _.rest(arguments);

    return function(target /* args ... */) {
      if (!_.exists(target)) {
        return undefined;
      }

      var methodName = nom(method).trim();
      var targetMethod = target[methodName];
      var args = _.rest(arguments);

      if (_.exists(targetMethod) && (method === targetMethod)) {
        return targetMethod.apply(target, _.cat(pargs, args));
      }
      else {
        return undefined;
      }
    };
  };

  // Builds a polymorphic function based on dispatch predicates.
  L.dispatcher = function (/* funs */) {
    var funs = _.toArray(arguments);
    var size = funs.length;

    return function(target /*, args */) {
      var ret = undefined;
      var args = _.rest(arguments);

      for (var funIndex = 0; funIndex < size; funIndex++) {
        var fun = funs[funIndex];
        ret = fun.apply(fun, cons(target, args));

        if (existy(ret)) return ret;
      }

      return ret;
    };
  };

  // Delicious curry
  // ---------------

  // Return functions having been partially applied with a single
  // argument, either as the first or last argument.
  //
  // *signature:* `(any* -> a) -> any* -> (any* -> a)`
  var bindRight = function(fun, arg) {
    return function(/* args... */) {
      return fun.apply({}, [].slice.call(arguments).concat(arg));
    };
  };

  var bindLeft = function(fun, arg) {
    return function(/* args... */) {
      return fun.apply({}, [arg].concat(_.toArray(arguments)));
    };
  };

  // Returns a function which will curry a function either
  // right to left or left to right.
  //
  // *signature:* `Number -> ((any* -> a) -> Number -> (any* -> a))`
  var currier = function(direction) {
  	var binder = direction == 1 ? bindLeft : bindRight;
  	return function(fun, count) {
  	  if( count === undefined ) {
  	    // if no count was provided, parse the function to count arguments
  	    count = fun.length;
  	  }
  	  if( count === 0 ) {
  	    // if there are no more arguments to apply, return the value.
  	    return fun();
  	  }
  	  return function(arg) {
  	    return L.curry(binder(fun, arg), count-1);
  	  };
  	};
  };

  // Returns the curried form of a function, either left to
  // right or right to left.
  //
  // *signature:* `(any* -> a) -> Number -> (any* -> a)`
  L.curry = currier(-1);
  L.leftCurry = currier(1);

  // Returns the partial application of a given
  // function, left to right.
  //
  // *signature:* `(any* -> a) -> any* -> (any* -> a)`
  L.partial = function(fun/*, args... */) {
    // partial applier of a given function
    return L.uncurry(L.uncurry(L.leftCurry(fun)).apply({}, [].slice.call(arguments, 1)));
  };

  // Returns the partial application of a list of arguments on a
  // curried function.
  //
  // *signature:* `(any* -> a) -> any* -> [(any* ->] a)`
  L.uncurry = function(fun) {
  	return function(/* args... */) {
  	  var resp = fun;
  	  [].forEach.call(arguments, function(arg) {
  	    resp = resp(arg);
  	  });
  	  return resp;
  	};
  };

  // Curries a function of `n` parameters from right to left.
  L.curry1 = L.fix1 = L.schonfinkel = L.curry(L.curry)(1);
  L.curry2 = L.fix2 = L.schonfinkel2 = L.curry(L.curry)(2);
  L.curry3 = L.fix3 = L.schonfinkel3 = L.curry(L.curry)(3);
  L.curry4 = L.fix4 = L.schonfinkel4 = L.curry(L.curry)(4);

  // parses ints
  L.parseInt = L.curry1(parseInt);

  // greater-than and less-than
  L.gt      = L.curry2(function(lhs, rhs) { return lhs > rhs; });
  L.lt      = L.curry2(function(lhs, rhs) { return lhs < rhs; });

  L.eq      = L.curry2(function(lhs, rhs) { return _.isEqual(lhs, rhs); });
  L.is      = L.curry2(function(lhs, rhs) { return lhs === rhs; });
  L.oneOf   = L.curry2(function(lhs, set) { return _.some(set, L.eq(lhs)); });

  // Partial application
  // -------------------

  // Partially applies a function by one argument from left to right
  L.partial1 = L.partial;
  L.partial2 = L.partial;

  // Rotates the arguments of a function. For example:
  //    L.rot(fun(a, b, c)) => fun(b, c, a)

  L.rot = function(fun) {
    return function(/* args */) {
      var head = _.first(arguments);
      var more = _.rest(arguments);
      return fun.apply(fun, _.cat(more, [head]));
    };
  };

  // Monadology
  // ----------

  // Creates a function that works within the context of the
  // `actions` logic.  `lift` accepts one or two functions:
  //
  // 1. a required answer function that serves as a way to calculate a
  //    result based on the given managed structure.
  // 2. an optional state function that 'modifies' the managed structure
  //    to represent its current state.
  //
  // If a state function is not given then the result given by the
  // answer function serves as the state value at each step.
  //
  L.lift = function(answerFun, stateFun) {
    return function(/* args */) {
      var args = _.toArray(arguments);

      return function(state) {
        var ans = answerFun.apply(null, _.cons(state, args));
        // If no stateFun given, just carry the answer forward
        var s = _.exists(stateFun) ? stateFun(state) : ans;

        return {answer: ans, state: s};
      };
    };
  };

  // Takes a number of curried functions that modifies
  // an intermediate description of mutation.  The final
  // function in the sequence is meant to return the final
  // value in the chain.
  L.actions = function (acts, done) {
    return function (seed) {
      var init = { values: [], state: seed };

      var intermediate = _.reduce(acts, function (stateObj, action) {
        var result = action(stateObj.state);
        var values = _.cat(stateObj.values, [result.answer]);
        return { values: values, state: result.state };
      }, init);

      var keep = _.filter(intermediate.values, _.exists);

      return done(keep, intermediate.state);
    };
  };

  // Ref-type mixin
  // -----------------

  // Mixes into a target object (destructively)

  L.mix = function(target /*, mixins */) {
    var args = _.rest(arguments);

    if (_.some(args))
      _.extend.apply(null, _.cons(target, args));

    return target;
  };

  // The Watchable mixin encapsulates the Subscriber/Observer
  // pattern
  L.WatchableMixin = (function () {
    var watchers = {};

    return {
      // The use of named function exprs is a temporary hack to make
      // these methods play nicely with `invoker`.  This is a non-portable
      // technique.
      notify: function notify(oldVal, newVal) {
        _.each(watchers, function(watcher, key) {
          watcher.call(this, key, oldVal, newVal);
        });

        return _.size(watchers);
      },
      // The watch function accepts a callback and
      // a descriptive key used for associating the
      // callback with a name of some sort.
      watch: function watch(key, fun) {
        watchers[key] = fun;
        return key;
      },
      // Takes the key and unsubscribes the watcher
      // associated with it.
      unwatch: function unwatch(key) {
        var old = watchers[key];
        watchers = _.omit(watchers, key);
        return old;
      }
    };
  })();

  // The functions for use with Watchable things
  L.addWatch    = L.meth(L.WatchableMixin.watch);
  L.removeWatch = L.meth(L.WatchableMixin.unwatch);

  // A hole is a mutable cell:
  //  * Takes an initial value
  //  * Accepts an optional validator
  L.Hole = function(init, validator) {
    if (validator && !validator(init))
      fail("Attempted to set invalid value " + init + " on construction");

    this._value = init;
    this._validator = validator;
  };

  // The Hole mixin describes the following change policy:
  //  * All value setting goes through the validator (if available)
  //  * Swaps take a function that accepts the current value and
  //    set the new value with its result
  //  * Snapshots should be values
  L.HoleMixin = {
    setValue: function setValue(newVal) {
      var validate = this._validator;
      var oldVal   = this._value;

      if (_.exists(validate))
        if (!validate(newVal))
          fail("Attempted to set invalid value " + newVal);

      this._value = newVal;
      this.notify(oldVal, newVal);
      return this._value;
    }
  };

  L.SwapMixin = {
    swap: function swap(fun /* , args... */) {
      return this.setValue(fun.apply(this, _.cons(this._value, _.rest(arguments))));
     },
    // Delegate down to the snapshot function
    snapshot: function snapshot() {
      return snapshot(this._value);
    }
  };

  L.mix(L.Hole.prototype,
        L.HoleMixin,
        L.SwapMixin,
        L.WatchableMixin);

  // The function delegates.
  L.setValue = L.meth(L.Hole.prototype.setValue);
  L.swap     = L.meth(L.Hole.prototype.swap);

  // CAS reference type
  // ------------------

  // A CAS is a Hole with compare-and-swap semantics
  L.CAS = function() {
    L.Hole.apply(this, arguments);
  };

  L.CASMixin = {
    // A compareAndSwap expects the caller to supply a
    // value that it expects is the current value. Change
    // fails if these values do not match.
    compareAndSwap: function compareAndSwap(oldVal, f) {
      if (this._value === oldVal) {
        this.setValue(f(this._value));
        return true;
      }
      else {
        return undefined;
      }
    }
  };

  L.mix(L.CAS.prototype,
        L.CASMixin,
        L.HoleMixin,
        L.SwapMixin,
        L.WatchableMixin);

  // The CAS function delegate.
  L.compareAndSwap = L.meth(L.CAS.prototype.compareAndSwap);

  // Checkers and conditions
  // --------------------------

  // *may get pulled out into another lib*

  // Creates a validation function: one that takes
  // some number of arguments and returns a truthy value
  // if it "looks good".  Checkers also have a `message`
  // field useful for building human-readble error messages.
  L.checker = function(message, fun) {
    var f = function(/* args */) {
      return fun.apply(fun, arguments);
    };

    f['message'] = message;
    return f;
  };

  function condition1(/* checkers */) {
    var checkers = _.toArray(arguments);

    return function(fun, arg) {
      var errors = mapcat(function(isValid) {
        return isValid(arg) ? [] : [isValid.message];
      }, checkers);

      if (!_.isEmpty(errors))
        throw new Error(errors.join(", "));

      return fun(arg);
    };
  };

  // Exports and sundries
  // --------------------

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = L;
    }
    exports.L = L;
  } else {
    root.L = L;
  }
}).call(this);
<commit_msg>Adding undermine stuff<commit_after>//    Lemonad.js
//    http://www.functionaljs.org
//    (c) 2013 Fogus, Ariadne Softworks
//    Lemonad may be freely distributed under the MIT license

// *Always start with the secret JavaScript sauce...*
;(function() {
  // Dependencies
  // ------------

  // I want to require Underscore.js if it's not already available.
  var _ = this._ || require('underscore-contrib');

  // Setup
  // -----

  // Establish the root object, `window` in the browser, or `global` on the server.

  var root = this;

  // The namespace object `L` is a function in its own right that returns
  // a partially applied function of an arbitrary number of arguments.
  // For example, you can use `L` to partially apply the first argument to the
  // `_.template` function:
  //
  //     var hi = L(_.template, "Hello <%= name %>");
  //     _.map([{name: 'Joe'}, {name: 'Bob'}, {name: 'Cleveland'}], hi);
  //
  //     //=> ["Hello Joe", "Hello Bob", "Hello Cleveland"]
  //
  // *signature:* `(any* -> a) -> any* -> (any* -> a)`

  var L = function(fun /*, args */) {
    var args = _.rest(arguments);

    if (_.some(args)) {
      // Default to the native `bind` if available
      if (_.exists(fun.bind)) {
        return fun.bind.apply(fun, _.cons(undefined, args));
      }

      return function(/* arguments */) {
        return fun.apply(null, args.concat(_.toArray(arguments)));
      };
    }
    else return L.curry(fun);
  };

  // Current version
  L.VERSION = '0.6.3';

  // Internal utilities
  // ------------------

  // A simple function to throw an `Error`
  function fail(str) {
    throw new Error(str);
  }

  // A temporary way to derive the name of a function. I hope to
  // eliminate the need sooner rather than later.
  //
  // *signature:* `(a -> b) -> string`

  function nom(fun) {
    var src = fun.toString();
    src = src.substr('function '.length);
    var n = src.substr(0, src.indexOf('('));

    if (!_.isFunction(fun)) fail("Cannot derive the name of a non-function.");
    if (fun.name && (fun.name === n)) return fun.name;
    if (n) return n;
    fail("Anonymous function has no name.");
  }

  // Delegates down to the snapshot method if an object has one
  // or clones the object outright if not.  This is not as robust
  // as it could be.  Explore the possibility of pulling in
  // Lo-Dash's impl.

  var snapshot = function(thing) {
    if (_.exists(thing) && _.exists(thing.snapshot))
      return thing.snapshot();
    else
      return _.snapshot(thing);
  };

  // Useful tools
  // ------------

  // Used as a unique value to signify certain conditions in Lemonad.
  // Also takes a number of args and returns an array of unique values.
  //
  // *signature:* `any* -> [any*]`

  L.$ = function(){
    return _.uniq(arguments);
  };

  // Array selectors
  // ---------------

  // A function to get at an index into an array
  L.nth = function(array, index) {
    if ((index < 0) || (index > array.length - 1)) throw Error("Attempting to index outside the bounds of the array.");

    return array[index];
  };

  // increment, decrement and halve
  //
  // *signature:* `number -> number`
  L.inc = function(n) { return n+1; };
  L.dec = function(n) { return n-1; };
  L.halve = function(n) { return n/2; };

  // Returns the type constructor
  //
  // *signature:* `{any: any} -> (any* -> any)`
  L.ctor = function(obj) {
    return _.exists(obj) ? obj.constructor : null;
  };

  // Useful predicates
  // -----------------

  // ### Type predicates

  // A reference is something that derives from a Lemonad Hole object
  //
  // *signature:* `a -> boolean`
  L.isReference = function(x) {
    return (x instanceof L.Hole) || (x instanceof L.CAS);
  };

  // Returns a function that will invoke the given method
  // on any object that it receives as an argument.
  L.meth = L.walterWhite = L.invoker = function(method /*, args*/) {
    var pargs = _.rest(arguments);

    return function(target /* args ... */) {
      if (!_.exists(target)) {
        return undefined;
      }

      var methodName = nom(method).trim();
      var targetMethod = target[methodName];
      var args = _.rest(arguments);

      if (_.exists(targetMethod) && (method === targetMethod)) {
        return targetMethod.apply(target, _.cat(pargs, args));
      }
      else {
        return undefined;
      }
    };
  };

  // Builds a polymorphic function based on dispatch predicates.
  L.dispatcher = function (/* funs */) {
    var funs = _.toArray(arguments);
    var size = funs.length;

    return function(target /*, args */) {
      var ret = undefined;
      var args = _.rest(arguments);

      for (var funIndex = 0; funIndex < size; funIndex++) {
        var fun = funs[funIndex];
        ret = fun.apply(fun, cons(target, args));

        if (existy(ret)) return ret;
      }

      return ret;
    };
  };

  // Delicious curry
  // ---------------

  // Return functions having been partially applied with a single
  // argument, either as the first or last argument.
  //
  // *signature:* `(any* -> a) -> any* -> (any* -> a)`
  var bindRight = function(fun, arg) {
    return function(/* args... */) {
      return fun.apply({}, [].slice.call(arguments).concat(arg));
    };
  };

  var bindLeft = function(fun, arg) {
    return function(/* args... */) {
      return fun.apply({}, [arg].concat(_.toArray(arguments)));
    };
  };

  // Returns a function which will curry a function either
  // right to left or left to right.
  //
  // *signature:* `Number -> ((any* -> a) -> Number -> (any* -> a))`
  var currier = function(direction) {
  	var binder = direction == 1 ? bindLeft : bindRight;
  	return function(fun, count) {
  	  if( count === undefined ) {
  	    // if no count was provided, parse the function to count arguments
  	    count = fun.length;
  	  }
  	  if( count === 0 ) {
  	    // if there are no more arguments to apply, return the value.
  	    return fun();
  	  }
  	  return function(arg) {
  	    return L.curry(binder(fun, arg), count-1);
  	  };
  	};
  };

  // Returns the curried form of a function, either left to
  // right or right to left.
  //
  // *signature:* `(any* -> a) -> Number -> (any* -> a)`
  L.curry = currier(-1);
  L.leftCurry = currier(1);

  // Returns the partial application of a given
  // function, left to right.
  //
  // *signature:* `(any* -> a) -> any* -> (any* -> a)`
  L.partial = function(fun/*, args... */) {
    // partial applier of a given function
    return L.uncurry(L.uncurry(L.leftCurry(fun)).apply({}, [].slice.call(arguments, 1)));
  };

  // Returns the partial application of a list of arguments on a
  // curried function.
  //
  // *signature:* `(any* -> a) -> any* -> [(any* ->] a)`
  L.uncurry = function(fun) {
  	return function(/* args... */) {
  	  var resp = fun;
  	  [].forEach.call(arguments, function(arg) {
  	    resp = resp(arg);
  	  });
  	  return resp;
  	};
  };

  // Curries a function of `n` parameters from right to left.
  L.curry1 = L.fix1 = L.schonfinkel = L.curry(L.curry)(1);
  L.curry2 = L.fix2 = L.schonfinkel2 = L.curry(L.curry)(2);
  L.curry3 = L.fix3 = L.schonfinkel3 = L.curry(L.curry)(3);
  L.curry4 = L.fix4 = L.schonfinkel4 = L.curry(L.curry)(4);

  // parses ints
  L.parseInt = L.curry1(parseInt);

  // greater-than and less-than
  L.gt      = L.curry2(function(lhs, rhs) { return lhs > rhs; });
  L.lt      = L.curry2(function(lhs, rhs) { return lhs < rhs; });

  L.eq      = L.curry2(function(lhs, rhs) { return _.isEqual(lhs, rhs); });
  L.is      = L.curry2(function(lhs, rhs) { return lhs === rhs; });
  L.oneOf   = L.curry2(function(lhs, set) { return _.some(set, L.eq(lhs)); });

  // Partial application
  // -------------------

  // Partially applies a function by one argument from left to right
  L.partial1 = L.partial;
  L.partial2 = L.partial;

  // Rotates the arguments of a function. For example:
  //    L.rot(fun(a, b, c)) => fun(b, c, a)

  L.rot = function(fun) {
    return function(/* args */) {
      var head = _.first(arguments);
      var more = _.rest(arguments);
      return fun.apply(fun, _.cat(more, [head]));
    };
  };

  // Monadology
  // ----------

  // Creates a function that works within the context of the
  // `actions` logic.  `lift` accepts one or two functions:
  //
  // 1. a required answer function that serves as a way to calculate a
  //    result based on the given managed structure.
  // 2. an optional state function that 'modifies' the managed structure
  //    to represent its current state.
  //
  // If a state function is not given then the result given by the
  // answer function serves as the state value at each step.
  //
  L.lift = function(answerFun, stateFun) {
    return function(/* args */) {
      var args = _.toArray(arguments);

      return function(state) {
        var ans = answerFun.apply(null, _.cons(state, args));
        // If no stateFun given, just carry the answer forward
        var s = _.exists(stateFun) ? stateFun(state) : ans;

        return {answer: ans, state: s};
      };
    };
  };

  // Takes a number of curried functions that modifies
  // an intermediate description of mutation.  The final
  // function in the sequence is meant to return the final
  // value in the chain.
  L.actions = function (acts, done) {
    return function (seed) {
      var init = { values: [], state: seed };

      var intermediate = _.reduce(acts, function (stateObj, action) {
        var result = action(stateObj.state);
        var values = _.cat(stateObj.values, [result.answer]);
        return { values: values, state: result.state };
      }, init);

      var keep = _.filter(intermediate.values, _.exists);

      return done(keep, intermediate.state);
    };
  };

  // Ref-type mixin
  // -----------------

  // Mixes into a target object (destructively)

  L.mix = function(target /*, mixins */) {
    var args = _.rest(arguments);

    if (_.some(args))
      _.extend.apply(null, _.cons(target, args));

    return target;
  };

  // The Watchable mixin encapsulates the Subscriber/Observer
  // pattern
  L.WatchableMixin = (function () {
    var watchers = {};

    return {
      // The use of named function exprs is a temporary hack to make
      // these methods play nicely with `invoker`.  This is a non-portable
      // technique.
      notify: function notify(oldVal, newVal) {
        _.each(watchers, function(watcher, key) {
          watcher.call(this, key, oldVal, newVal);
        });

        return _.size(watchers);
      },
      // The watch function accepts a callback and
      // a descriptive key used for associating the
      // callback with a name of some sort.
      watch: function watch(key, fun) {
        watchers[key] = fun;
        return key;
      },
      // Takes the key and unsubscribes the watcher
      // associated with it.
      unwatch: function unwatch(key) {
        var old = watchers[key];
        watchers = _.omit(watchers, key);
        return old;
      }
    };
  })();

  // The functions for use with Watchable things
  L.addWatch    = L.meth(L.WatchableMixin.watch);
  L.removeWatch = L.meth(L.WatchableMixin.unwatch);

  // A hole is a mutable cell:
  //  * Takes an initial value
  //  * Accepts an optional validator
  L.Hole = function(init, validator) {
    if (validator && !validator(init))
      fail("Attempted to set invalid value " + init + " on construction");

    this._value = init;
    this._validator = validator;
  };

  // The Hole mixin describes the following change policy:
  //  * All value setting goes through the validator (if available)
  //  * Swaps take a function that accepts the current value and
  //    set the new value with its result
  //  * Snapshots should be values
  L.HoleMixin = {
    setValue: function setValue(newVal) {
      var validate = this._validator;
      var oldVal   = this._value;

      if (_.exists(validate))
        if (!validate(newVal))
          fail("Attempted to set invalid value " + newVal);

      this._value = newVal;
      this.notify(oldVal, newVal);
      return this._value;
    }
  };

  L.SwapMixin = {
    swap: function swap(fun /* , args... */) {
      return this.setValue(fun.apply(this, _.cons(this._value, _.rest(arguments))));
     },
    // Delegate down to the snapshot function
    snapshot: function snapshot() {
      return snapshot(this._value);
    }
  };

  L.mix(L.Hole.prototype,
        L.HoleMixin,
        L.SwapMixin,
        L.WatchableMixin);

  // The function delegates.
  L.setValue = L.meth(L.Hole.prototype.setValue);
  L.swap     = L.meth(L.Hole.prototype.swap);

  // CAS reference type
  // ------------------

  // A CAS is a Hole with compare-and-swap semantics
  L.CAS = function() {
    L.Hole.apply(this, arguments);
  };

  L.CASMixin = {
    // A compareAndSwap expects the caller to supply a
    // value that it expects is the current value. Change
    // fails if these values do not match.
    compareAndSwap: function compareAndSwap(oldVal, f) {
      if (this._value === oldVal) {
        this.setValue(f(this._value));
        return true;
      }
      else {
        return undefined;
      }
    }
  };

  L.mix(L.CAS.prototype,
        L.CASMixin,
        L.HoleMixin,
        L.SwapMixin,
        L.WatchableMixin);

  // The CAS function delegate.
  L.compareAndSwap = L.meth(L.CAS.prototype.compareAndSwap);

  // Checkers and conditions
  // --------------------------

  // *may get pulled out into another lib*

  // Creates a validation function: one that takes
  // some number of arguments and returns a truthy value
  // if it "looks good".  Checkers also have a `message`
  // field useful for building human-readble error messages.
  L.checker = function(message, fun) {
    var f = function(/* args */) {
      return fun.apply(fun, arguments);
    };

    f['message'] = message;
    return f;
  };

  function condition1(/* checkers */) {
    var checkers = _.toArray(arguments);

    return function(fun, arg) {
      var errors = mapcat(function(isValid) {
        return isValid(arg) ? [] : [isValid.message];
      }, checkers);

      if (!_.isEmpty(errors))
        throw new Error(errors.join(", "));

      return fun(arg);
    };
  };

  // Exports and sundries
  // --------------------

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = L;
    }
    exports.L = L;
  } else {
    root.L = L;
  }
}).call(this);
<|endoftext|>


<commit_before>/* ************************************************************************
 *
 *    qxcompiler - node.js based replacement for the Qooxdoo python
 *    toolchain
 *
 *    https://github.com/johnspackman/qxcompiler
 *
 *    Copyright:
 *      2011-2016 Zenesis Limited, http://www.zenesis.com
 *
 *    License:
 *      MIT: http://opensource.org/licenses/MIT.
 *
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com)
 *
 * *********************************************************************** */

/* eslint no-confusing-arrow: 0 */

var fs = require("fs");
var path = require("path");
var util = require("util");
var child_process = require("child_process");
var async = require("async");

module.exports = util;

var log = null;

/** *****************************************************************************
 *
 * DEBUG LOGGING
 *
 */
(function() {
  var LM = require("../utils/LogManager").getInstance();

  module.exports.createLog = function(categoryName) {
    if (!categoryName) {
      categoryName = "generic";
    }
    return LM.getLogger(categoryName);
  };
})();

log = module.exports.createLog("util");

/** *****************************************************************************
 *
 * EXCEPTIONS
 *
 */
function AbstractError(msg, code, constr) {
  Error.captureStackTrace(this, constr || this);
  this.message = msg || "Error";
  this.code = code || "EUNKNOWN";
}
util.inherits(AbstractError, Error);
module.exports.AbstractError = AbstractError;

function GenericError(msg, code) {
  GenericError.super_.call(this, msg, code, this.constructor);
}
util.inherits(GenericError, util.AbstractError);
module.exports.GenericError = GenericError;

/** *****************************************************************************
 *
 * UTILS
 *
 */
module.exports.merge = function() {
  var args = [].slice.call(arguments);
  function mergeInto(dest, src) {
    for (var name in src) {
      var value = src[name];
      if (typeof value == "object") {
        if (value instanceof Date) {
          dest[name] = value;
        } else {
          var current = dest[name];
          if (current === null || current === undefined) {
            current = dest[name] = {};
          }
          mergeInto(current, value);
        }
      } else {
        dest[name] = value;
      }
    }
  }

  for (var i = 1; i < args.length; i++) {
    mergeInto(args[0], args[i]);
  }

  return args[0];
};

module.exports.process = {
  /**
   *
   * @param cmd
   * @param args
   * @param opts
   * @param callback
   * @returns
   */
  exec : function(cmd/* , args, opts, callback */) {
    var tmp = [].slice.call(arguments, 1);
    var args = Array.isArray(tmp[0]) ? tmp.shift() : [];
    var callback = typeof tmp[tmp.length - 1] === "function" ? tmp.pop() : null;
    var opts = (typeof tmp[0] === "object" ? tmp.shift() : null)||{};

    log.debug("spawn: " + cmd + " " + JSON.stringify(args));
    var proc = child_process.spawn(cmd, args);
    var lnrStdout;
    var lnrStderr;
    proc.stdout.on("data", lnrStdout = function(data) {
      log.trace(cmd + " stdout: " + data);
      if (opts.onStdout) {
        opts.onStdout(data);
      }
    });
    proc.stderr.on("data", lnrStderr = function(data) {
      log.trace(cmd + " stderr: " + data);
      if (opts.onStderr) {
        opts.onStderr(data);
      }
    });
    var closed = false;
    var exited = false;
    var exitCode = null;
    var exitSignal = null;
    proc.on("close", function() {
      log.trace(cmd + " closed");
      proc.stdout.removeListener("data", lnrStdout);
      proc.stderr.removeListener("data", lnrStderr);
      closed = true;
      if (closed && exited && callback) {
        callback(null, exitCode, exitSignal);
        callback = null;
      }
    });
    proc.on("exit", function(code, signal) {
      log.trace(cmd + " exit code=" + code + ", signal=" + signal);
      exited = true;
      exitCode = code;
      exitSignal = signal;
      if (closed && exited && callback) {
        callback(null, exitCode, signal);
        callback = null;
      }
    });
    proc.on("error", function(err) {
      if (callback) {
        callback(err);
      } else {
        throw err;
      }
    });
    return proc;
  },

  execAndCapture : function(cmd, args, callback) {
    var stdout = [];
    function capture(data) {
      stdout.push(data);
    }
    var proc = this.exec(cmd, args, {
      onStdout: capture,
      onStderr: capture
    }, function(err, code, signal) {
      callback(err, stdout.join(""), code, signal);
    });
    return proc;
  }
};

/** *****************************************************************************
 *
 * JSON
 *
 */
module.exports.json = {
  parseISO : function(str) {
    if (!str) {
      return null;
    }
    var m = str.match(/([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).([0-9]+)Z/);
    if (!m) {
      log.error("Cannot parse date " + str);
      return null;
    }
    var year = parseInt(m[1], 10);
    var month = parseInt(m[2], 10) - 1;
    var day = parseInt(m[3], 10);
    var hours = parseInt(m[4], 10);
    var minutes = parseInt(m[5], 10);
    var seconds = parseInt(m[6], 10);
    var millis = parseInt(m[7], 10);

    var dt = new Date(year, month, day, hours, minutes, seconds, millis);
    var offset = dt.getTimezoneOffset();
    if (offset != 0) {
      dt = new Date(year, month, day, hours, minutes - offset, seconds, millis);
    }
    return dt;
  },

  formatISO : function(dt) {
    if (!dt) {
      return null;
    }
    function dp2(v) {
      if (v < 10) {
        return "0" + v;
      }
      return String(v);
    }
    function dp3(v) {
      if (v < 10) {
        return "00" + v;
      }
      if (v < 100) {
        return "0" + v;
      }
      return String(v);
    }
    var str = dt.getUTCFullYear() + "-" + dp2(dt.getUTCMonth() + 1) + "-" + dp2(dt.getUTCDate()) + "T" + dp2(dt.getUTCHours()) +
        ":" + dp2(dt.getUTCMinutes()) + ":" + dp2(dt.getUTCSeconds()) + "." + dp3(dt.getUTCMilliseconds()) + "Z";
    return str;
  },

  parse : function(str, reviver) {
    var t = this;
    var PREFIX = this.__PREFIX__;
    var SUFFIX = this.__SUFFIX__;

    function reviverImpl(key, value) {
      if (typeof value === "string" && value.substring(0, PREFIX.length) === PREFIX && value.slice(-SUFFIX.length) === SUFFIX) {
        var str = value.slice(PREFIX.length, -SUFFIX.length);
        if (str.slice(0, 5) == "Date(" && str.slice(-1) == ")") {
          var strDt = str.slice(5, -1);
          var dt = t.parseISO(strDt);
          if (dt) {
            if (t.formatISO(dt) != strDt) {
              log.error("date parsing (iso), original text=" + strDt + ", expected=" + t.formatISO(dt) + ", interpretted Date=" +
                  dt);
            } else if (dt.getTime() > (new Date().getTime()) + (24 * 60 * 60 * 1000)) {
              log.debug("date parsing (future), str=" + str + ", strDt=" + strDt + ", dt=" + dt + ", dt.time=" + dt.getTime() +
                  ", now.time=" + (new Date().getTime()));
            }
          }
          return dt;
        }
      }
      if (typeof reviver == "function") {
        return reviver(key, value);
      }
      return value;
    }

    return JSON.parse(str, reviverImpl);
  },

  stringify : function(obj, replacer, space) {
    var t = this;
    var PREFIX = this.__PREFIX__;
    var SUFFIX = this.__SUFFIX__;

    function replacerImpl(key, value) {
      if (this[key] instanceof Date) {
        value = PREFIX + "Date(" + t.formatISO(this[key]) + ")" + SUFFIX;
      }
      if (typeof replacer == "function") {
        value = replacer(key, value);
      }
      return value;
    }

    return JSON.stringify(obj, replacerImpl, space);
  },

  __PREFIX__ : "[__GRASSHOPPER__[",
  __SUFFIX__ : "]]"
};

/**
 * Encodes a path, IE only encodes the parts between '/' and excluding the query
 */
module.exports.encodeURIPath = function(uri) {
  var pos = uri.indexOf("?");
  var query = "";
  if (pos > -1) {
    query = uri.substring(pos);
    uri = uri.substring(0, pos);
  }
  var segs = uri.split("/");
  for (var i = 0; i < segs.length; i++) {
    segs[i] = encodeURIComponent(segs[i]);
  }
  return segs.join("/") + query;
};

/**
 * Creates a dir
 * @param dir
 * @param cb
 */
function mkpath(dir, cb) {
  dir = path.normalize(dir);
  var segs = dir.split(path.sep);
  var made = "";
  async.eachSeries(
    segs,
    function(seg, cb) {
      if (made.length || !seg.length) {
        made += "/";
      }
      made += seg;
      fs.exists(made, function(exists) {
        if (!exists) {
          fs.mkdir(made, function(err) {
            if (err && err.code === "EEXIST") {
              err = null;
            }
            cb(err);
          });
          return;
        }
        fs.stat(made, function(err, stat) {
          if (err) {
            cb(err);
          } else if (stat.isDirectory()) {
            cb(null);
          } else {
            cb(new Error("Cannot create " + made + " (in " + dir + ") because it exists and is not a directory", "ENOENT"));
          }
        });
      });
    },
    function(err) {
      cb(err);
    });
}

function mkParentPath(dir, cb) {
  var segs = dir.split(/[\\\/]/);
  segs.pop();
  if (!segs.length) {
    return cb && cb();
  }
  dir = segs.join(path.sep);
  return mkpath(dir, cb);
}

function newExternalPromise() {
  var resolve;
  var reject;
  var promise = new Promise((resolve_, reject_) => {
    resolve = resolve_;
    reject = reject_;
  });
  promise.resolve = resolve;
  promise.reject = reject;
  return promise;
}

/*
function promisify(fn, ...args) {
  return new Promise((resolve, reject) => {
    args = args.slice();
    args.push(function(err, result) {
      if (err)
        reject(err);
      else
        resolve(result);
    });
    try {
      fn.apply(null, args);
    } catch(ex) {
      reject(ex);
    }
  });
}
module.exports.promisify = promisify;
*/

function promisifyThis(fn, self, ...args) {
  return new Promise((resolve, reject) => {
    args = args.slice();
    args.push(function(err, result) {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
    try {
      fn.apply(self, args);
    } catch (ex) {
      reject(ex);
    }
  });
}

module.exports.mkpath = mkpath;
module.exports.mkpathAsync = filename => new Promise((resolve, reject) => {
  mkpath(filename, err => err ? reject(err) : resolve());
});
module.exports.mkParentPath = mkParentPath;
module.exports.mkParentPathAsync = filename => new Promise((resolve, reject) => {
  mkParentPath(filename, err => err ? reject(err) : resolve());
});
module.exports.newExternalPromise = newExternalPromise;
module.exports.promisifyThis = promisifyThis;

<commit_msg>ignore @qooxdoo/qx/no-illegal-private-usage change GRASSHPOPER to qooxdoo<commit_after>/* ************************************************************************
 *
 *    qxcompiler - node.js based replacement for the Qooxdoo python
 *    toolchain
 *
 *    https://github.com/johnspackman/qxcompiler
 *
 *    Copyright:
 *      2011-2016 Zenesis Limited, http://www.zenesis.com
 *
 *    License:
 *      MIT: http://opensource.org/licenses/MIT.
 *
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com)
 *
 * *********************************************************************** */

/* eslint no-confusing-arrow: 0 */
/* eslint @qooxdoo/qx/no-illegal-private-usage: off */
var fs = require("fs");
var path = require("path");
var util = require("util");
var child_process = require("child_process");
var async = require("async");

module.exports = util;

var log = null;

/** *****************************************************************************
 *
 * DEBUG LOGGING
 *
 */
(function() {
  var LM = require("../utils/LogManager").getInstance();

  module.exports.createLog = function(categoryName) {
    if (!categoryName) {
      categoryName = "generic";
    }
    return LM.getLogger(categoryName);
  };
})();

log = module.exports.createLog("util");

/** *****************************************************************************
 *
 * EXCEPTIONS
 *
 */
function AbstractError(msg, code, constr) {
  Error.captureStackTrace(this, constr || this);
  this.message = msg || "Error";
  this.code = code || "EUNKNOWN";
}
util.inherits(AbstractError, Error);
module.exports.AbstractError = AbstractError;

function GenericError(msg, code) {
  GenericError.super_.call(this, msg, code, this.constructor);
}
util.inherits(GenericError, util.AbstractError);
module.exports.GenericError = GenericError;

/** *****************************************************************************
 *
 * UTILS
 *
 */
module.exports.merge = function() {
  var args = [].slice.call(arguments);
  function mergeInto(dest, src) {
    for (var name in src) {
      var value = src[name];
      if (typeof value == "object") {
        if (value instanceof Date) {
          dest[name] = value;
        } else {
          var current = dest[name];
          if (current === null || current === undefined) {
            current = dest[name] = {};
          }
          mergeInto(current, value);
        }
      } else {
        dest[name] = value;
      }
    }
  }

  for (var i = 1; i < args.length; i++) {
    mergeInto(args[0], args[i]);
  }

  return args[0];
};

module.exports.process = {
  /**
   *
   * @param cmd
   * @param args
   * @param opts
   * @param callback
   * @returns
   */
  exec : function(cmd/* , args, opts, callback */) {
    var tmp = [].slice.call(arguments, 1);
    var args = Array.isArray(tmp[0]) ? tmp.shift() : [];
    var callback = typeof tmp[tmp.length - 1] === "function" ? tmp.pop() : null;
    var opts = (typeof tmp[0] === "object" ? tmp.shift() : null)||{};

    log.debug("spawn: " + cmd + " " + JSON.stringify(args));
    var proc = child_process.spawn(cmd, args);
    var lnrStdout;
    var lnrStderr;
    proc.stdout.on("data", lnrStdout = function(data) {
      log.trace(cmd + " stdout: " + data);
      if (opts.onStdout) {
        opts.onStdout(data);
      }
    });
    proc.stderr.on("data", lnrStderr = function(data) {
      log.trace(cmd + " stderr: " + data);
      if (opts.onStderr) {
        opts.onStderr(data);
      }
    });
    var closed = false;
    var exited = false;
    var exitCode = null;
    var exitSignal = null;
    proc.on("close", function() {
      log.trace(cmd + " closed");
      proc.stdout.removeListener("data", lnrStdout);
      proc.stderr.removeListener("data", lnrStderr);
      closed = true;
      if (closed && exited && callback) {
        callback(null, exitCode, exitSignal);
        callback = null;
      }
    });
    proc.on("exit", function(code, signal) {
      log.trace(cmd + " exit code=" + code + ", signal=" + signal);
      exited = true;
      exitCode = code;
      exitSignal = signal;
      if (closed && exited && callback) {
        callback(null, exitCode, signal);
        callback = null;
      }
    });
    proc.on("error", function(err) {
      if (callback) {
        callback(err);
      } else {
        throw err;
      }
    });
    return proc;
  },

  execAndCapture : function(cmd, args, callback) {
    var stdout = [];
    function capture(data) {
      stdout.push(data);
    }
    var proc = this.exec(cmd, args, {
      onStdout: capture,
      onStderr: capture
    }, function(err, code, signal) {
      callback(err, stdout.join(""), code, signal);
    });
    return proc;
  }
};

/** *****************************************************************************
 *
 * JSON
 *
 */
module.exports.json = {
  parseISO : function(str) {
    if (!str) {
      return null;
    }
    var m = str.match(/([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).([0-9]+)Z/);
    if (!m) {
      log.error("Cannot parse date " + str);
      return null;
    }
    var year = parseInt(m[1], 10);
    var month = parseInt(m[2], 10) - 1;
    var day = parseInt(m[3], 10);
    var hours = parseInt(m[4], 10);
    var minutes = parseInt(m[5], 10);
    var seconds = parseInt(m[6], 10);
    var millis = parseInt(m[7], 10);

    var dt = new Date(year, month, day, hours, minutes, seconds, millis);
    var offset = dt.getTimezoneOffset();
    if (offset != 0) {
      dt = new Date(year, month, day, hours, minutes - offset, seconds, millis);
    }
    return dt;
  },

  formatISO : function(dt) {
    if (!dt) {
      return null;
    }
    function dp2(v) {
      if (v < 10) {
        return "0" + v;
      }
      return String(v);
    }
    function dp3(v) {
      if (v < 10) {
        return "00" + v;
      }
      if (v < 100) {
        return "0" + v;
      }
      return String(v);
    }
    var str = dt.getUTCFullYear() + "-" + dp2(dt.getUTCMonth() + 1) + "-" + dp2(dt.getUTCDate()) + "T" + dp2(dt.getUTCHours()) +
        ":" + dp2(dt.getUTCMinutes()) + ":" + dp2(dt.getUTCSeconds()) + "." + dp3(dt.getUTCMilliseconds()) + "Z";
    return str;
  },

  parse : function(str, reviver) {
    var t = this;
    var PREFIX = this.__PREFIX;
    var SUFFIX = this.__SUFFIX;

    function reviverImpl(key, value) {
      if (typeof value === "string" && value.substring(0, PREFIX.length) === PREFIX && value.slice(-SUFFIX.length) === SUFFIX) {
        var str = value.slice(PREFIX.length, -SUFFIX.length);
        if (str.slice(0, 5) == "Date(" && str.slice(-1) == ")") {
          var strDt = str.slice(5, -1);
          var dt = t.parseISO(strDt);
          if (dt) {
            if (t.formatISO(dt) != strDt) {
              log.error("date parsing (iso), original text=" + strDt + ", expected=" + t.formatISO(dt) + ", interpretted Date=" +
                  dt);
            } else if (dt.getTime() > (new Date().getTime()) + (24 * 60 * 60 * 1000)) {
              log.debug("date parsing (future), str=" + str + ", strDt=" + strDt + ", dt=" + dt + ", dt.time=" + dt.getTime() +
                  ", now.time=" + (new Date().getTime()));
            }
          }
          return dt;
        }
      }
      if (typeof reviver == "function") {
        return reviver(key, value);
      }
      return value;
    }

    return JSON.parse(str, reviverImpl);
  },

  stringify : function(obj, replacer, space) {
    var t = this;
    var PREFIX = this.__PREFIX;
    var SUFFIX = this.__SUFFIX;

    function replacerImpl(key, value) {
      if (this[key] instanceof Date) {
        value = PREFIX + "Date(" + t.formatISO(this[key]) + ")" + SUFFIX;
      }
      if (typeof replacer == "function") {
        value = replacer(key, value);
      }
      return value;
    }

    return JSON.stringify(obj, replacerImpl, space);
  },

  __PREFIX : "[__QOOXDOO__[",
  __SUFFIX : "]]"
};

/**
 * Encodes a path, IE only encodes the parts between '/' and excluding the query
 */
module.exports.encodeURIPath = function(uri) {
  var pos = uri.indexOf("?");
  var query = "";
  if (pos > -1) {
    query = uri.substring(pos);
    uri = uri.substring(0, pos);
  }
  var segs = uri.split("/");
  for (var i = 0; i < segs.length; i++) {
    segs[i] = encodeURIComponent(segs[i]);
  }
  return segs.join("/") + query;
};

/**
 * Creates a dir
 * @param dir
 * @param cb
 */
function mkpath(dir, cb) {
  dir = path.normalize(dir);
  var segs = dir.split(path.sep);
  var made = "";
  async.eachSeries(
    segs,
    function(seg, cb) {
      if (made.length || !seg.length) {
        made += "/";
      }
      made += seg;
      fs.exists(made, function(exists) {
        if (!exists) {
          fs.mkdir(made, function(err) {
            if (err && err.code === "EEXIST") {
              err = null;
            }
            cb(err);
          });
          return;
        }
        fs.stat(made, function(err, stat) {
          if (err) {
            cb(err);
          } else if (stat.isDirectory()) {
            cb(null);
          } else {
            cb(new Error("Cannot create " + made + " (in " + dir + ") because it exists and is not a directory", "ENOENT"));
          }
        });
      });
    },
    function(err) {
      cb(err);
    });
}

function mkParentPath(dir, cb) {
  var segs = dir.split(/[\\\/]/);
  segs.pop();
  if (!segs.length) {
    return cb && cb();
  }
  dir = segs.join(path.sep);
  return mkpath(dir, cb);
}

function newExternalPromise() {
  var resolve;
  var reject;
  var promise = new Promise((resolve_, reject_) => {
    resolve = resolve_;
    reject = reject_;
  });
  promise.resolve = resolve;
  promise.reject = reject;
  return promise;
}

/*
function promisify(fn, ...args) {
  return new Promise((resolve, reject) => {
    args = args.slice();
    args.push(function(err, result) {
      if (err)
        reject(err);
      else
        resolve(result);
    });
    try {
      fn.apply(null, args);
    } catch(ex) {
      reject(ex);
    }
  });
}
module.exports.promisify = promisify;
*/

function promisifyThis(fn, self, ...args) {
  return new Promise((resolve, reject) => {
    args = args.slice();
    args.push(function(err, result) {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
    try {
      fn.apply(self, args);
    } catch (ex) {
      reject(ex);
    }
  });
}

module.exports.mkpath = mkpath;
module.exports.mkpathAsync = filename => new Promise((resolve, reject) => {
  mkpath(filename, err => err ? reject(err) : resolve());
});
module.exports.mkParentPath = mkParentPath;
module.exports.mkParentPathAsync = filename => new Promise((resolve, reject) => {
  mkParentPath(filename, err => err ? reject(err) : resolve());
});
module.exports.newExternalPromise = newExternalPromise;
module.exports.promisifyThis = promisifyThis;

<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { Route } from 'react-router-dom';
import Link from 'components/Link';
import { logout } from 'redux/actions/UserActions';
import {
  getNumberOfUnreadMessages,
  increaseNumberOfUnreadMessages,
  turnChatOff,
  resetChat
} from 'redux/actions/ChatActions';
import {
  changeSocketStatus,
  checkVersion,
  notifyChatSubjectChange,
  increaseNumNewPosts,
  increaseNumNewNotis
} from 'redux/actions/NotiActions';
import { closeSearch, initSearch } from 'redux/actions/SearchActions';
import AccountMenu from './AccountMenu';
import ChatButton from './ChatButton';
import Icon from 'components/Icon';
import { GENERAL_CHAT_ID } from 'constants/database';
import SearchBox from '../SearchBox';
import HeaderNav from './HeaderNav';
import { css } from 'emotion';
import { Color, desktopMinWidth } from 'constants/css';
import { socket } from 'constants/io';
import { recordUserAction } from 'helpers/userDataHelpers';
import { container, logo } from './Styles';

class Header extends Component {
  static propTypes = {
    chatLoading: PropTypes.bool,
    chatMode: PropTypes.bool,
    changeSocketStatus: PropTypes.func,
    checkVersion: PropTypes.func,
    history: PropTypes.object.isRequired,
    getNumberOfUnreadMessages: PropTypes.func,
    increaseNumNewPosts: PropTypes.func,
    increaseNumNewNotis: PropTypes.func,
    increaseNumberOfUnreadMessages: PropTypes.func,
    initSearch: PropTypes.func,
    location: PropTypes.object,
    loggedIn: PropTypes.bool,
    logout: PropTypes.func,
    mobileNavbarShown: PropTypes.bool,
    notifyChatSubjectChange: PropTypes.func,
    numChatUnreads: PropTypes.number,
    numNewNotis: PropTypes.number,
    numNewPosts: PropTypes.number,
    onChatButtonClick: PropTypes.func,
    closeSearch: PropTypes.func.isRequired,
    onMobileMenuOpen: PropTypes.func,
    resetChat: PropTypes.func,
    searchBoxRef: PropTypes.func,
    searchMode: PropTypes.bool,
    showUpdateNotice: PropTypes.func,
    style: PropTypes.object,
    totalRewardAmount: PropTypes.number,
    turnChatOff: PropTypes.func,
    userId: PropTypes.number,
    username: PropTypes.string,
    versionMatch: PropTypes.bool
  };

  state = {
    notificationsMenuShown: false,
    feedLoading: false
  };

  componentDidMount() {
    const {
      increaseNumNewPosts,
      increaseNumNewNotis,
      increaseNumberOfUnreadMessages,
      checkVersion,
      changeSocketStatus
    } = this.props;
    socket.on('connect', () => {
      console.log('connected to socket');
      changeSocketStatus(true);
      checkVersion();
      if (this.props.userId) {
        socket.emit(
          'bind_uid_to_socket',
          this.props.userId,
          this.props.username
        );
      }
    });
    socket.on('receive_message', data => {
      if (
        !this.props.chatMode &&
        data.channelId !== GENERAL_CHAT_ID &&
        data.userId !== this.props.userId
      ) {
        increaseNumberOfUnreadMessages();
      }
    });
    socket.on('chat_invitation', data => {
      socket.emit('join_chat_channel', data.channelId);
      if (!this.props.chatMode) increaseNumberOfUnreadMessages();
    });
    socket.on('disconnect', () => {
      console.log('disconnected from socket');
      changeSocketStatus(false);
    });
    socket.on('subject_change', this.onSubjectChange);
    socket.on('new_story_post', increaseNumNewPosts);
    socket.on('new_notification', increaseNumNewNotis);
  }

  componentDidUpdate(prevProps) {
    const {
      chatMode,
      userId,
      username,
      getNumberOfUnreadMessages,
      showUpdateNotice,
      versionMatch
    } = this.props;
    if (userId && !prevProps.userId) {
      socket.connect();
      socket.emit('bind_uid_to_socket', userId, username);
    }
    if (!userId && prevProps.userId) {
      socket.disconnect();
    }
    if (userId && userId !== prevProps.userId) {
      getNumberOfUnreadMessages();
    }
    if (userId && chatMode !== prevProps.chatMode && chatMode === false) {
      getNumberOfUnreadMessages();
    }
    if (versionMatch !== prevProps.versionMatch) {
      showUpdateNotice(versionMatch);
    }
    if (userId !== prevProps.userId) {
      if (prevProps.userId !== null) {
        socket.emit('leave_my_notification_channel', prevProps.userId);
      }
      socket.emit('enter_my_notification_channel', userId);
    }
  }

  render() {
    const {
      chatLoading,
      location: { pathname },
      history,
      loggedIn,
      username,
      chatMode,
      initSearch,
      onChatButtonClick,
      onMobileMenuOpen,
      mobileNavbarShown,
      numChatUnreads,
      numNewNotis,
      numNewPosts,
      closeSearch,
      searchBoxRef,
      searchMode,
      style = {},
      totalRewardAmount,
      turnChatOff
    } = this.props;
    const isUsername =
      pathname.split('/')[1] !== 'videos' &&
      ['links', 'twinklexp'].indexOf(pathname.split('/')[1]) === -1 &&
      pathname.length > 1;
    return (

        {chatMode && (

            <Icon icon="times" />

Tap to close chat

        )}
        {!chatMode && (

            <Route
              to="/"
              children={({ match }) => {
                return (
                  <Link onClick={closeSearch} to="/">

                         0
                              ? {
                                  color: Color.gold()
                                }
                              : {}
                          }
                          className="logo logo-twin"
                        >
                          Twin

                         0
                              ? {
                                  color: Color.gold()
                                }
                              : {}
                          }
                          className="logo logo-kle"
                        >
                          kle

                  </Link>
                );
              }}
            />
            <>
              <HeaderNav
                className={`${searchMode || chatLoading ? 'hidden' : 'mobile'}`}
                alert={numNewNotis > 0 || totalRewardAmount > 0}
                alertColor={Color.pink()}
                imgLabel="user"
                onClick={onMobileMenuOpen}
              />
              <div
                className={`header-nav ${chatLoading ? 'hidden' : 'mobile'}`}
                onClick={searchMode ? closeSearch : initSearch}
                style={{ width: searchMode && '10%' }}
              >

                  {searchMode ? <Icon icon="times" /> : <Icon icon="search" />}

              <HeaderNav
                to="/"
                onClick={() => {
                  closeSearch();
                  window.scrollTo(0, 0);
                }}
                isHome
                className={chatLoading || searchMode ? 'hidden' : 'mobile'}
                imgLabel="home"
                alert={numNewPosts > 0}
                isUsername={isUsername}
              >
                Home
              </HeaderNav>
              <HeaderNav
                to="/videos"
                onClick={closeSearch}
                className={chatLoading || searchMode ? 'desktop' : ''}
                imgLabel="film"
              >
                Watch
              </HeaderNav>
              <HeaderNav
                to="/links"
                onClick={closeSearch}
                className={`${chatLoading || searchMode ? 'desktop' : ''} ${css`
                  @media (min-width: ${desktopMinWidth}) {
                    margin-right: 1rem;
                    margin-left: 2rem;
                  }
                `}`}
                imgLabel="book"
              >
                Read
              </HeaderNav>
            </>

              <SearchBox
                innerRef={searchBoxRef}
                onFocus={initSearch}
                style={{
                  width: '95%'
                }}
              />

               0 && Color.pink()
                }}
              >
                <Icon icon="comments" />

            <div className={`header-nav ${chatLoading ? 'mobile' : 'hidden'}`}>
              Loading...

              <ChatButton
                className="desktop"
                onClick={onChatButtonClick}
                chatMode={chatMode}
                loading={chatLoading}
                numUnreads={numChatUnreads}
              />

            <AccountMenu
              className={`desktop ${css`
                @media (min-width: ${desktopMinWidth}) {
                  margin-left: 0.5rem;
                }
              `}`}
              history={history}
              loggedIn={loggedIn}
              logout={this.onLogout}
              title={username}
            />

        )}

    );
  }

  onLogoClick = () => {
    if (this.props.chatMode) {
      this.props.turnChatOff();
    }
  };

  onLogout = () => {
    const { logout, resetChat } = this.props;
    recordUserAction({ action: 'logout' });
    logout();
    resetChat();
  };

  onSubjectChange = ({ subject }) => {
    const { notifyChatSubjectChange } = this.props;
    notifyChatSubjectChange(subject);
  };
}

export default connect(
  state => ({
    loggedIn: state.UserReducer.loggedIn,
    username: state.UserReducer.username,
    userType: state.UserReducer.userType,
    userId: state.UserReducer.userId,
    mobileNavbarShown: state.ViewReducer.mobileNavbarShown,
    numNewNotis: state.NotiReducer.numNewNotis,
    numNewPosts: state.NotiReducer.numNewPosts,
    numChatUnreads: state.ChatReducer.numUnreads,
    chatMode: state.ChatReducer.chatMode,
    searchMode: state.SearchReducer.searchMode,
    totalRewardAmount: state.NotiReducer.totalRewardAmount,
    versionMatch: state.NotiReducer.versionMatch
  }),
  {
    changeSocketStatus,
    checkVersion,
    getNumberOfUnreadMessages,
    increaseNumNewPosts,
    increaseNumNewNotis,
    increaseNumberOfUnreadMessages,
    initSearch,
    logout,
    notifyChatSubjectChange,
    closeSearch,
    resetChat,
    turnChatOff
  }
)(withRouter(Header));
<commit_msg>minor scrolling issue fix<commit_after>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { Route } from 'react-router-dom';
import Link from 'components/Link';
import { logout } from 'redux/actions/UserActions';
import {
  getNumberOfUnreadMessages,
  increaseNumberOfUnreadMessages,
  turnChatOff,
  resetChat
} from 'redux/actions/ChatActions';
import {
  changeSocketStatus,
  checkVersion,
  notifyChatSubjectChange,
  increaseNumNewPosts,
  increaseNumNewNotis
} from 'redux/actions/NotiActions';
import { closeSearch, initSearch } from 'redux/actions/SearchActions';
import AccountMenu from './AccountMenu';
import ChatButton from './ChatButton';
import Icon from 'components/Icon';
import { GENERAL_CHAT_ID } from 'constants/database';
import SearchBox from '../SearchBox';
import HeaderNav from './HeaderNav';
import { css } from 'emotion';
import { Color, desktopMinWidth } from 'constants/css';
import { socket } from 'constants/io';
import { recordUserAction } from 'helpers/userDataHelpers';
import { container, logo } from './Styles';

class Header extends Component {
  static propTypes = {
    chatLoading: PropTypes.bool,
    chatMode: PropTypes.bool,
    changeSocketStatus: PropTypes.func,
    checkVersion: PropTypes.func,
    history: PropTypes.object.isRequired,
    getNumberOfUnreadMessages: PropTypes.func,
    increaseNumNewPosts: PropTypes.func,
    increaseNumNewNotis: PropTypes.func,
    increaseNumberOfUnreadMessages: PropTypes.func,
    initSearch: PropTypes.func,
    location: PropTypes.object,
    loggedIn: PropTypes.bool,
    logout: PropTypes.func,
    mobileNavbarShown: PropTypes.bool,
    notifyChatSubjectChange: PropTypes.func,
    numChatUnreads: PropTypes.number,
    numNewNotis: PropTypes.number,
    numNewPosts: PropTypes.number,
    onChatButtonClick: PropTypes.func,
    closeSearch: PropTypes.func.isRequired,
    onMobileMenuOpen: PropTypes.func,
    resetChat: PropTypes.func,
    searchBoxRef: PropTypes.func,
    searchMode: PropTypes.bool,
    showUpdateNotice: PropTypes.func,
    style: PropTypes.object,
    totalRewardAmount: PropTypes.number,
    turnChatOff: PropTypes.func,
    userId: PropTypes.number,
    username: PropTypes.string,
    versionMatch: PropTypes.bool
  };

  state = {
    notificationsMenuShown: false,
    feedLoading: false
  };

  componentDidMount() {
    const {
      increaseNumNewPosts,
      increaseNumNewNotis,
      increaseNumberOfUnreadMessages,
      checkVersion,
      changeSocketStatus
    } = this.props;
    socket.on('connect', () => {
      console.log('connected to socket');
      changeSocketStatus(true);
      checkVersion();
      if (this.props.userId) {
        socket.emit(
          'bind_uid_to_socket',
          this.props.userId,
          this.props.username
        );
      }
    });
    socket.on('receive_message', data => {
      if (
        !this.props.chatMode &&
        data.channelId !== GENERAL_CHAT_ID &&
        data.userId !== this.props.userId
      ) {
        increaseNumberOfUnreadMessages();
      }
    });
    socket.on('chat_invitation', data => {
      socket.emit('join_chat_channel', data.channelId);
      if (!this.props.chatMode) increaseNumberOfUnreadMessages();
    });
    socket.on('disconnect', () => {
      console.log('disconnected from socket');
      changeSocketStatus(false);
    });
    socket.on('subject_change', this.onSubjectChange);
    socket.on('new_story_post', increaseNumNewPosts);
    socket.on('new_notification', increaseNumNewNotis);
  }

  componentDidUpdate(prevProps) {
    const {
      chatMode,
      userId,
      username,
      getNumberOfUnreadMessages,
      showUpdateNotice,
      versionMatch
    } = this.props;
    if (userId && !prevProps.userId) {
      socket.connect();
      socket.emit('bind_uid_to_socket', userId, username);
    }
    if (!userId && prevProps.userId) {
      socket.disconnect();
    }
    if (userId && userId !== prevProps.userId) {
      getNumberOfUnreadMessages();
    }
    if (userId && chatMode !== prevProps.chatMode && chatMode === false) {
      getNumberOfUnreadMessages();
    }
    if (versionMatch !== prevProps.versionMatch) {
      showUpdateNotice(versionMatch);
    }
    if (userId !== prevProps.userId) {
      if (prevProps.userId !== null) {
        socket.emit('leave_my_notification_channel', prevProps.userId);
      }
      socket.emit('enter_my_notification_channel', userId);
    }
  }

  render() {
    const {
      chatLoading,
      location: { pathname },
      history,
      loggedIn,
      username,
      chatMode,
      initSearch,
      onChatButtonClick,
      onMobileMenuOpen,
      mobileNavbarShown,
      numChatUnreads,
      numNewNotis,
      numNewPosts,
      closeSearch,
      searchBoxRef,
      searchMode,
      style = {},
      totalRewardAmount,
      turnChatOff
    } = this.props;
    const isUsername =
      pathname.split('/')[1] !== 'videos' &&
      ['links', 'twinklexp'].indexOf(pathname.split('/')[1]) === -1 &&
      pathname.length > 1;
    return (

        {chatMode && (

            <Icon icon="times" />

Tap to close chat

        )}
        {!chatMode && (

            <Route
              to="/"
              children={({ match }) => {
                return (
                  <Link onClick={closeSearch} to="/">

                         0
                              ? {
                                  color: Color.gold()
                                }
                              : {}
                          }
                          className="logo logo-twin"
                        >
                          Twin

                         0
                              ? {
                                  color: Color.gold()
                                }
                              : {}
                          }
                          className="logo logo-kle"
                        >
                          kle

                  </Link>
                );
              }}
            />
            <>
              <HeaderNav
                className={`${searchMode || chatLoading ? 'hidden' : 'mobile'}`}
                alert={numNewNotis > 0 || totalRewardAmount > 0}
                alertColor={Color.pink()}
                imgLabel="user"
                onClick={onMobileMenuOpen}
              />
              <div
                className={`header-nav ${chatLoading ? 'hidden' : 'mobile'}`}
                onClick={searchMode ? closeSearch : initSearch}
                style={{ width: searchMode && '10%' }}
              >

                  {searchMode ? <Icon icon="times" /> : <Icon icon="search" />}

              <HeaderNav
                to="/"
                onClick={() => {
                  closeSearch();
                  window.scrollTo(0, 0);
                }}
                isHome
                className={chatLoading || searchMode ? 'hidden' : 'mobile'}
                imgLabel="home"
                alert={numNewPosts > 0}
                isUsername={isUsername}
              >
                Home
              </HeaderNav>
              <HeaderNav
                to="/videos"
                onClick={closeSearch}
                className={chatLoading || searchMode ? 'desktop' : ''}
                imgLabel="film"
              >
                Watch
              </HeaderNav>
              <HeaderNav
                to="/links"
                onClick={closeSearch}
                className={`${chatLoading || searchMode ? 'desktop' : ''} ${css`
                  @media (min-width: ${desktopMinWidth}) {
                    margin-right: 1rem;
                    margin-left: 2rem;
                  }
                `}`}
                imgLabel="book"
              >
                Read
              </HeaderNav>
            </>

              <SearchBox
                innerRef={searchBoxRef}
                onFocus={initSearch}
                style={{
                  width: '95%'
                }}
              />

               0 && Color.pink()
                }}
              >
                <Icon icon="comments" />

            <div className={`header-nav ${chatLoading ? 'mobile' : 'hidden'}`}>
              Loading...

              <ChatButton
                className="desktop"
                onClick={onChatButtonClick}
                chatMode={chatMode}
                loading={chatLoading}
                numUnreads={numChatUnreads}
              />

            <AccountMenu
              className={`desktop ${css`
                @media (min-width: ${desktopMinWidth}) {
                  margin-left: 0.5rem;
                }
              `}`}
              history={history}
              loggedIn={loggedIn}
              logout={this.onLogout}
              title={username}
            />

        )}

    );
  }

  onLogoClick = () => {
    if (this.props.chatMode) {
      this.props.turnChatOff();
    }
    document.getElementById('App').scrollTop = 0;
  };

  onLogout = () => {
    const { logout, resetChat } = this.props;
    recordUserAction({ action: 'logout' });
    logout();
    resetChat();
  };

  onSubjectChange = ({ subject }) => {
    const { notifyChatSubjectChange } = this.props;
    notifyChatSubjectChange(subject);
  };
}

export default connect(
  state => ({
    loggedIn: state.UserReducer.loggedIn,
    username: state.UserReducer.username,
    userType: state.UserReducer.userType,
    userId: state.UserReducer.userId,
    mobileNavbarShown: state.ViewReducer.mobileNavbarShown,
    numNewNotis: state.NotiReducer.numNewNotis,
    numNewPosts: state.NotiReducer.numNewPosts,
    numChatUnreads: state.ChatReducer.numUnreads,
    chatMode: state.ChatReducer.chatMode,
    searchMode: state.SearchReducer.searchMode,
    totalRewardAmount: state.NotiReducer.totalRewardAmount,
    versionMatch: state.NotiReducer.versionMatch
  }),
  {
    changeSocketStatus,
    checkVersion,
    getNumberOfUnreadMessages,
    increaseNumNewPosts,
    increaseNumNewNotis,
    increaseNumberOfUnreadMessages,
    initSearch,
    logout,
    notifyChatSubjectChange,
    closeSearch,
    resetChat,
    turnChatOff
  }
)(withRouter(Header));
<|endoftext|>


<commit_before><commit_msg>Deleted file<commit_after><|endoftext|>


<commit_before>
var mime = require( 'mime-lib' )

/**
 * [filter description]
 * @param  {[type]} key   [description]
 * @param  {[type]} value [description]
 * @return {[type]}       [description]
 */
function filter( key, value ) {

  for( var i in filter.map ) {
    if( ~filter.map[i].indexOf( key ) )
      return filter.fn[i]( value )
  }

  return value

}

/**
 * Maps header field names
 * to their filter functions.
 *
 * @type {Object}
 */
filter.map = {
  address: [ 'from', 'reply_to', 'to', 'cc', 'bcc', 'sender' ],
  subject: [ 'subject' ],
  content_type: [ 'content_type' ]
}

/**
 * Filter functions
 *
 * @type {Object}
 */
filter.fn = {

  /**
   * Desperately tries to get a name
   * from a contact definition.
   *
   * @param  {String} input
   * @return {Object}
   */
  address: function( input ) {

    // Address formats
    var patterns = [
      // "Example Name" <hello@example.com>
      [ /"([^"]+)"\s+<([^>]+)>/, 2, 1 ],
      // 'Example Name' <hello@example.com>
      [ /'([^']+)'\s+<([^>]+)>/, 2, 1 ],
      // Example Name <hello@example.com>
      [ /(.+)\s+<([^>]+)>/, 2, 1 ],
      // <hello@example.com> (Example Name)
      [ /([^\s]+)\s+[(][^)]+[)]/, 1, 2 ],
      // hello@example.com (Example Name)
      [ /<([^>]+)>\s+[(][^)]+[)]/, 1, 2 ],
      // <hello@example.com>
      [ /<([^>]+)>/, 1, 1e2 ],
      // hello@example.com
      [ /.*/, 0, 1e2 ]
      // " <- Syntax highlighter fix (Sublime Text 2)
    ]

    var pattern, i, m

    for( var fmt in patterns ) {
      i = patterns[ fmt ]
      if( m = i[0].exec( input ) ) {
        input = {
          address: m[ i[1] ] || null,
          name: m[ i[2] ] || null
        }
        break
      }
    }

    return input

  },

  /**
   * Strips 'Re:' and 'Fwd:' from the subject line,
   * the "reply" or "forwarded" status should be determined
   * through the mail headers anyway...
   *
   * Also, subject lines like "Fwd: Re: Re: Re: Actual subject"
   * are fucking annoying.
   *
   * @param  {String} input
   * @return {String}
   */
  subject: function( input ) {

    return mime.decodeWord( input )

    return input.replace(
      /(Re): |(Fwd): /gi,
      ''
      // function( match, re, fwd ) {
      //   console.log( match, !!re, !!fwd );
      // }
    )
  },

  /**
   * Converts mime strings like
   * `text/plain; charset="utf-8"; format="fixed"`
   * to an object of this form:
   *
   *     {
   *       mime: 'text/plain',
   *       charset: 'utf-8',
   *       format: 'fixed'
   *     }
   *
   * @param  {String} input
   * @return {Object}
   */
  content_type: function( input ) {

    var pattern = /^(.*?)([=](['"]?)(.*)\3)?$/
    var m, object = {}

    input = input.split( '; ' )
    object.mime = input.shift()

    for( var i in input ) {
      if( m = pattern.exec( input[i] ) ) {
        if( m[4] ) object[ m[1] ] = m[4]
      }
    }

    return object

  },

}

module.exports = filter
<commit_msg>Fixed filter#address() in lib/header/filter.js<commit_after>
var mime = require( 'mime-lib' )

/**
 * [filter description]
 * @param  {[type]} key   [description]
 * @param  {[type]} value [description]
 * @return {[type]}       [description]
 */
function filter( key, value ) {

  for( var i in filter.map ) {
    if( ~filter.map[i].indexOf( key ) )
      return filter.fn[i]( value )
  }

  return value

}

/**
 * Maps header field names
 * to their filter functions.
 *
 * @type {Object}
 */
filter.map = {
  address: [ 'from', 'reply_to', 'to', 'cc', 'bcc', 'sender' ],
  subject: [ 'subject' ],
  content_type: [ 'content_type' ]
}

/**
 * Filter functions
 *
 * @type {Object}
 */
filter.fn = {

  /**
   * Desperately tries to get a name
   * from a contact definition.
   *
   * @param  {String} input
   * @return {Object}
   */
  address: function address( input ) {

    // Run over multiple addresses
    if( ~input.indexOf( ',' ) )
      return input.split( ',' ).map( address )

    // Address formats
    var patterns = [
      // "Example Name" <hello@example.com>
      [ /"([^"]+)"\s+<([^>]+)>/, 2, 1 ],
      // 'Example Name' <hello@example.com>
      [ /'([^']+)'\s+<([^>]+)>/, 2, 1 ],
      // Example Name <hello@example.com>
      [ /(.+)\s+<([^>]+)>/, 2, 1 ],
      // <hello@example.com> (Example Name)
      [ /([^\s]+)\s+[(][^)]+[)]/, 1, 2 ],
      // hello@example.com (Example Name)
      [ /<([^>]+)>\s+[(][^)]+[)]/, 1, 2 ],
      // <hello@example.com>
      [ /<([^>]+)>/, 1, 1e2 ],
      // hello@example.com
      [ /.*/, 0, 1e2 ]
      // " <- Syntax highlighter fix (Sublime Text 2)
    ]

    var pattern, fmt, i, m

    for( fmt in patterns ) {
      i = patterns[ fmt ]
      if( m = i[0].exec( input ) ) {
        input = {
          address: m[ i[1] ] || null,
          name: m[ i[2] ] || null
        }
        break
      }
    }

    return input

  },

  /**
   * Strips 'Re:' and 'Fwd:' from the subject line,
   * the "reply" or "forwarded" status should be determined
   * through the mail headers anyway...
   *
   * Also, subject lines like "Fwd: Re: Re: Re: Actual subject"
   * are fucking annoying.
   *
   * @param  {String} input
   * @return {String}
   */
  subject: function( input ) {

    return mime.decodeWord( input )

    return input.replace(
      /(Re): |(Fwd): /gi,
      ''
      // function( match, re, fwd ) {
      //   console.log( match, !!re, !!fwd );
      // }
    )
  },

  /**
   * Converts mime strings like
   * `text/plain; charset="utf-8"; format="fixed"`
   * to an object of this form:
   *
   *     {
   *       mime: 'text/plain',
   *       charset: 'utf-8',
   *       format: 'fixed'
   *     }
   *
   * @param  {String} input
   * @return {Object}
   */
  content_type: function( input ) {

    var pattern = /^(.*?)([=](['"]?)(.*)\3)?$/
    var m, object = {}

    input = input.split( '; ' )
    object.mime = input.shift()

    for( var i in input ) {
      if( m = pattern.exec( input[i] ) ) {
        if( m[4] ) object[ m[1] ] = m[4]
      }
    }

    return object

  },

}

module.exports = filter
<|endoftext|>


<commit_before>'use strict';

const registrationRegExp = /^(\+|-)\s*((\s*@[\S]+)*)$/;

const RED_PLAYERS = ':red_circle:';
const BLUE_PLAYERS = ':large_blue_circle:';

class Registration {
	constructor( context, timeToLive ) {
		// Default time to live is 8 hrs
		this.timeToLive = timeToLive ? timeToLive : 8 * 60 * 60 * 1000;
		this.context = context;
		this.registered = [];
	}

	handleRequest( request, asyncResponse ) {
		if ( this.cleanRegistered() ) {
			asyncResponse( request.response_url, 'Old users were removed from queue.' );
		}

		const registered = this.registered;
		const registrationValues = registrationRegExp.exec( request.resolvedText );
		const context = this.context;

		if ( !registrationValues ) {
			return;
		}

		const users = registrationValues[ 2 ].split( '@' )
			.filter( user => user.length )
			.map( ( user ) => user.trim() );

		if ( !users.length ) {
			users.push( request.user_name );
		}

		const isAdd = registrationValues[ 1 ] == '+';

		for ( let user of users ) {
			if ( isAdd ) {
				add( user );
			} else {
				remove( user );
			}
		}

		const usersMessage = ( users.map( user => '@' + user ).join( ', ' ) );

		return {
			'response_type': 'ephemeral',
			'text': `${usersMessage} have been ${( isAdd ? 'added to' : 'removed from' )} the next match.`
		};

		function add( user ) {
			// Add the player only if he/she is not registered already.
			// We could return if user in in registered pool already but it could be abused to check if someone else is already registered.
			if ( hasUser( registered, user ) ) {
				asyncResponse( request.response_url, 'Oops! Some users already registered and will not be re-added!', 'ephemeral' );
				return;
			}

			registered.push( { name: user, date: new Date() } );

			const playersCount = registered.length % 4;

			if ( playersCount ) {
				asyncResponse( request.response_url, 'A new player joined the next match! ' +
					'Waiting for the next *' + ( 4 - playersCount ) + '*!' );
			} else {
				const players = registered.splice( -4 );

				players.sort( ( playerA, playerB ) => {
					return context.rank.getPlayer( playerA.name ).score - context.rank.getPlayer( playerB.name ).score;
				} );

				const expected = context.rank.getExpected( players[ 0 ].name, players[ 3 ].name, players[ 1 ].name, players[ 2 ].name );

				const isTeamARed = Math.random() >= 0.5;
				const teamAColor = isTeamARed ? RED_PLAYERS : BLUE_PLAYERS;
				const teamBColor = isTeamARed ? BLUE_PLAYERS : RED_PLAYERS;

				asyncResponse( request.response_url,
					':fire: ' +
					'@' + players[ 0 ].name + ' @' + players[ 3 ].name +
					' ' + teamAColor +
					' (' + expected.red + ' : ' + expected.blue + ') ' +
					' ' + teamBColor +
					'@' + players[ 1 ].name + ' @' + players[ 2 ].name
				);
			}
		}

		function remove( user ) {
			const index = registered.map( ( registeredUser ) => registeredUser.name ).lastIndexOf( user );

			if ( index > -1 ) {
				registered.splice( index, 1 );

				const playersCount = registered.length % 4;

				asyncResponse( request.response_url, 'User removed from the match. :chicken: ' +
					'Now we need ' + ( 4 - playersCount ) + ' players.' );
			}
		}

		function hasUser( registered, user ) {
			return registered.map( ( registeredUser ) => registeredUser.name ).indexOf( user ) !== -1;
		}
	}

	cleanRegistered() {
		const now = new Date();

		const registeredBefore = this.registered.length;

		this.registered = this.registered.filter( ( registeredUser ) => {
			return ( now.getTime() - registeredUser.date.getTime() ) < this.timeToLive;
		} );

		return this.registered.length !== registeredBefore;
	}
}

module.exports = Registration;
<commit_msg>Refactor match start response.<commit_after>'use strict';

const registrationRegExp = /^(\+|-)\s*((\s*@[\S]+)*)$/;

const RED_PLAYERS = ':red_circle:';
const BLUE_PLAYERS = ':large_blue_circle:';

class Registration {
	constructor( context, timeToLive ) {
		// Default time to live is 8 hrs
		this.timeToLive = timeToLive ? timeToLive : 8 * 60 * 60 * 1000;
		this.context = context;
		this.registered = [];
	}

	handleRequest( request, asyncResponse ) {
		if ( this.cleanRegistered() ) {
			asyncResponse( request.response_url, 'Old users were removed from queue.' );
		}

		const registered = this.registered;
		const registrationValues = registrationRegExp.exec( request.resolvedText );
		const context = this.context;

		if ( !registrationValues ) {
			return;
		}

		const users = registrationValues[ 2 ].split( '@' )
			.filter( user => user.length )
			.map( ( user ) => user.trim() );

		if ( !users.length ) {
			users.push( request.user_name );
		}

		const isAdd = registrationValues[ 1 ] == '+';

		for ( let user of users ) {
			if ( isAdd ) {
				add( user );
			} else {
				remove( user );
			}
		}

		const usersMessage = ( users.map( user => '@' + user ).join( ', ' ) );

		return {
			'response_type': 'ephemeral',
			'text': `${usersMessage} have been ${( isAdd ? 'added to' : 'removed from' )} the next match.`
		};

		function add( user ) {
			// Add the player only if he/she is not registered already.
			// We could return if user in in registered pool already but it could be abused to check if someone else is already registered.
			if ( hasUser( registered, user ) ) {
				asyncResponse( request.response_url, 'Oops! Some users already registered and will not be re-added!', 'ephemeral' );
				return;
			}

			registered.push( { name: user, date: new Date() } );

			const playersCount = registered.length % 4;

			if ( playersCount ) {
				asyncResponse( request.response_url, 'A new player joined the next match! ' +
					'Waiting for the next *' + ( 4 - playersCount ) + '*!' );
			} else {
				const players = registered.splice( -4 );

				players.sort( ( playerA, playerB ) => {
					return context.rank.getPlayer( playerA.name ).score - context.rank.getPlayer( playerB.name ).score;
				} );

				const expected = context.rank.getExpected( players[ 0 ].name, players[ 3 ].name, players[ 1 ].name, players[ 2 ].name );

				const isTeamARed = Math.random() >= 0.5;
				const teamAColor = isTeamARed ? RED_PLAYERS : BLUE_PLAYERS;
				const teamBColor = isTeamARed ? BLUE_PLAYERS : RED_PLAYERS;

				const teamA = `@${players[ 0 ].name} @${players[ 3 ].name}`;
				const teamB = `@${players[ 1 ].name} @${players[ 2 ].name}`;
				const expectedString = `(${expected.red} : ${expected.blue})`;

				asyncResponse( request.response_url, `:fire: ${teamA} ${teamAColor} ${expectedString} ${teamBColor} ${teamB}` );
			}
		}

		function remove( user ) {
			const index = registered.map( ( registeredUser ) => registeredUser.name ).lastIndexOf( user );

			if ( index > -1 ) {
				registered.splice( index, 1 );

				const playersCount = registered.length % 4;

				asyncResponse( request.response_url, 'User removed from the match. :chicken: ' +
					'Now we need ' + ( 4 - playersCount ) + ' players.' );
			}
		}

		function hasUser( registered, user ) {
			return registered.map( ( registeredUser ) => registeredUser.name ).indexOf( user ) !== -1;
		}
	}

	cleanRegistered() {
		const now = new Date();

		const registeredBefore = this.registered.length;

		this.registered = this.registered.filter( ( registeredUser ) => {
			return ( now.getTime() - registeredUser.date.getTime() ) < this.timeToLive;
		} );

		return this.registered.length !== registeredBefore;
	}
}

module.exports = Registration;
<|endoftext|>


<commit_before>angular.module('app').component('navigation',{
   templateUrl: 'app/template/navigation.html',
   controller: function($http, $filter){
       var ctrl = this;
       ctrl.links = [{
           href: '#/home',
           name: 'Home'
       }];
       ctrl.components = [{
           href: '#/table',
           name: 'Basic Table'
       },{
           href: '#/table-with-search',
           name: 'Basic Table + Search'
       },{
           href: '#/navbar',
           name: 'NavBar'
       },{
           href: '#/button',
           name: 'Button'
       },{
           href: '#/input',
           name: 'Input'
       },{
           href: '#/password',
           name: 'Password'
       },{
           href: '#/checkbox',
           name: 'Checkbox'
       },{
           href: '#/textarea',
           name: 'Textarea'
       },{
           href: '#/radio',
           name: 'Radio'
       },{
           href: '#/select',
           name: 'Select'
       },{
           href: '#/typeahead',
           name: 'Typeahead'
       },{
           href: '#/timepicker',
           name: 'Timepicker'
       },{
           href: '#/chart/line-chart',
           name: 'Line Chart'
       },{
           href: '#/chart/bar-chart',
           name: 'Bar Chart'
       },{
           href: '#/modal',
           name: 'Modal'
       },{
           href: '#/file-input',
           name: 'File Input'
       },{
           href: '#/slider',
           name: 'Slider'
       },{
           href: '#/accordian',
           name: 'Accordian'
       },{
           href: '#/alert',
           name: 'Alert'
       }];
       ctrl.components = $filter('orderBy')(ctrl.components, 'name');
   }
});<commit_msg>finishing up datepicker<commit_after>angular.module('app').component('navigation',{
   templateUrl: 'app/template/navigation.html',
   controller: function($http, $filter){
       var ctrl = this;
       ctrl.links = [{
           href: '#/home',
           name: 'Home'
       }];
       ctrl.components = [{
           href: '#/table',
           name: 'Basic Table'
       },{
           href: '#/table-with-search',
           name: 'Basic Table + Search'
       },{
           href: '#/navbar',
           name: 'NavBar'
       },{
           href: '#/button',
           name: 'Button'
       },{
           href: '#/input',
           name: 'Input'
       },{
           href: '#/password',
           name: 'Password'
       },{
           href: '#/checkbox',
           name: 'Checkbox'
       },{
           href: '#/textarea',
           name: 'Textarea'
       },{
           href: '#/radio',
           name: 'Radio'
       },{
           href: '#/select',
           name: 'Select'
       },{
           href: '#/typeahead',
           name: 'Typeahead'
       },{
           href: '#/timepicker',
           name: 'Timepicker'
       },{
           href: '#/chart/line-chart',
           name: 'Line Chart'
       },{
           href: '#/chart/bar-chart',
           name: 'Bar Chart'
       },{
           href: '#/modal',
           name: 'Modal'
       },{
           href: '#/file-input',
           name: 'File Input'
       },{
           href: '#/slider',
           name: 'Slider'
       },{
           href: '#/accordian',
           name: 'Accordian'
       },{
           href: '#/alert',
           name: 'Alert'
       },{
           href: '#/datepicker',
           name: 'Datepicker'
       },{
           href: '#/datepicker-popup',
           name: 'Datepicker Popup'
       }];
       ctrl.components = $filter('orderBy')(ctrl.components, 'name');
   }
});<|endoftext|>


<commit_before>import React from 'react';
import { browserHistory, Link } from 'react-router';

class Dashboard extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      interested: this.props.interested,
      inProgress: this.props.inProgress,
      complete: this.props.complete,
      jobContent: this.props.jobContent
    }
  }

  render() {
    return (

Dashboard

    )
  }

}

module.exports = Dashboard;
<commit_msg>passes props to job lists for each tab<commit_after>import React from 'react';
import { browserHistory, Link } from 'react-router';
import JobList from './JobList';
import JobListItem from './JobListItem';

class Dashboard extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      interested: this.props.interested,
      inProgress: this.props.inProgress,
      complete: this.props.complete,
      jobContent: this.props.jobContent,
      key: 1
    }
  }

  handleSelect(key) {
    this.setState({ key });
  }

  render() {
    return (
      <Tabs activeKey={this.state.key} onSelect={this.handleSelect} class="dashtabs">
        <Tab eventKey={1} title="Jobs I'm Interested In">
          <JobList
            jobContent={this.state.jobContent}
            interested={this.state.interested}
            addJobToInProgress={this.props.addJobToInProgress}
            addJobToComplete={this.props.addJobToComplete}
            removeJob={this.props.removeJob}
          />
        </Tab>
        <Tab eventKey={2} title="Job Applications In Progress">
          <JobList
            jobContent={this.state.jobContent}
            inProgress={this.state.inProgress}
            addJobToComplete={this.props.addJobToComplete}
            removeJob={this.props.removeJob}
          />

        </Tab>
        <Tab eventKey={3} title="Job Applications Complete">
          <JobList
            jobContent={this.state.jobContent}
            complete={this.state.complete}
            removeJob={this.props.removeJob}
          />
        </Tab>
      </Tabs>
    )
  }

}

module.exports = Dashboard;
<|endoftext|>