<commit_before>/**
# Exemplar.js — gradual typing by example
Copyright ©2016-2017 Tonio Loewald

Relatively painless gradual strong typing by example.

### exemplar: create type checkers by example

    exemplar(example_value)

Creates an exemplar that tests for values of the same type as `example_value`.

e.g.

    const numType = exemplar(1)

`numType` will return true for numbers and false for anything other than a
number.

    exemplar(some_function)

Creates an exemplar that behaves like the function, but it's an exemplar.

**Aside**: why use exemplars rather than just boolean test functions?
**exemplars** can be *specified*, combined, and also describe themselves.
They can also be used to drive the `childproof` function.

### Exemplar Methods

    exemplar.args(array)

Creates an exemplar for testing a sequence of arguments for childproofing a
function.
(Use types.noarguments for functions expecting no arguments.)

    exemplar.array(example)

Creates an exemplar that accepts an array of elements matching the example.

    exemplar.heteroArray(example, another_example, ...)

Creates an exemplar that accepts an array of elements matching one of the
provided examples.

    exemplar.regex(regular_expression)

Creates an exemplar that accepts strings that match the regular expression
provided.

    exemplar(...).describe(new_description)

Allows you to specify (override) the description of the method.

    exemplar(...).check(subject)

spams console.error if the subject is rejected

    exemplar(...).test(subject)

throws if the subject is rejected

### Specifiers

Specifiers allow you to tighten up an exemplar to accept very specific things.
E.g.

    exemplar({foo: 13})

creates an exemplar that accepts *any* object. But:

    exemplar({}).properties({foo: 13})

creates an exemplar that only accepts object with a property `foo` that is a
number.

    exemplar(...).optional()

will also accept `undefined`.

    exemplar(...).nullable()

will also accept `null`.

    exemplar(...).nonempty()

must have a numeric, positive length property.

#### Numeric specifiers

    exemplar(...).nonzero()
    exemplar(...).positive()
    exemplar(...).whole()
    exemplar(...).among()

#### Combining specifiers

    exemplar(...).and(exemplar_or_test_function)
    exemplar(...).or(exemplar_or_test_function)
    exemplar(...).not()

#### Object Specifiers

    exemplar(...).properties(object_example)

requires the object to have properties matching the example.

~~~~
const {exemplar} = _required_;
const numType = exemplar(1);
Test(() => numType(-11)).shouldBe(true);
Test(() => numType(Math.PI)).shouldBe(true);
Test(() => numType('3.14')).shouldBe(false);
Test(() => numType(true)).shouldBe(false);

const objType = exemplar({});
Test(() => objType({})).shouldBe(true);
Test(() => objType('test')).shouldBe(false);
Test(() => objType([])).shouldBe(false);

const geoType = objType.properties({name: 'fred', position:{x: 10, y: 100}});
Test(() => geoType({})).shouldBe(false);
Test(() => geoType({name: 'anne', position: {}})).shouldBe(true);

const geoType2 = objType.properties({name: 'fred',
position:objType.properties({x: 10, y: 100})});
Test(() => geoType2({name: 'anne', position: {}})).shouldBe(false);
Test(() => geoType2({name: 'anne', position: {x: -5, y: 33}})).shouldBe(true);
~~~~

### Childproof Functions

    childproof(function, expects, returns)

Produces a *childproofed* function that will throw if passed arguments other
that it expects
or if it returns a value other than expected (returns is optional — if omitted,
the function
is expected *not* to return a value).

expects or returns are treated as type references if a string if passed, and
treated as
*/
/* global module, console */
(function(module) {

  'use strict';

  const AND = (a, b) => a && b;
  const OR = (a, b) => a || b;

  const specifiers = {
    isExemplar: true,
    optional: function() {
      const test = this;
      return exemplar(subject => test(subject) || types.undefined(subject))
          .describe(test.description + ' (optional)');
    },
    nullable: function() {
      const test = this;
      return exemplar(subject => test(subject) || types.null(subject))
          .describe(test.description + ' OR null');
    },
    nonempty: function() {
      const test = this;
      return exemplar(
                 subject => test(subject) && types.number(subject.length) &&
                     subject.length > 0)
          .describe('non empty ' + test.description);
    },
    nonzero: function() {
      const test = this;
      return exemplar(subject => test(subject) && subject !== 0)
          .describe('non-zero ' + test.description);
    },
    positive: function() {
      const test = this;
      return exemplar(subject => test(subject) && subject > 0)
          .describe(test.descriotion = ' (>0)');
    },
    whole: function() {
      const test = this;
      return exemplar(subject => test(subject) && types.int(subject));
    },
    among: function(options) {
      const test = this;
      return exemplar(subject => test(subject) && options.indexOf(subject) > -1)
          .describe(`(${options.join('|')})`);
    },
    and: function(otherTest) {
      const test = this;
      return exemplar(subject => test(subject) && otherTest(subject))
          .describe(`${test.description} and ${otherTest.description}`);
    },
    or: function(otherTest) {
      const test = this;
      return exemplar(subject => test(subject) || otherTest(subject))
          .describe(`${test.description} or ${otherTest.description}`);
    },
    not: function() {
      const test = this;
      return exemplar(subject => !test(subject))
          .describe('not ' + test.description);
    },
    properties: function(object) {
      const test = this;
      const keys = Object.keys(object);
      const test_map = {};
      keys.forEach(key => test_map[key] = exemplar(object[key]));
      const checkProps = subject =>
          keys.map(key => test_map[key](subject[key])).reduce(AND);
      const description = test.description + ' with properties ' +
          keys.map(key => `key (${test_map[key].description})`).join(', ');
      return exemplar(subject => test(subject) && checkProps(subject))
          .describe(description);
    },
    describe: function(description) {
      this.description = description;
      return this;
    },
    check: subject => {
      if (!this(subject)) {
        console.error('expected', this.description);
      }
    },
    test: subject => {
      if (!this(subject)) {
        throw 'expected ' + this.description;
      }
    },
  };
  Object.freeze(specifiers);

  function exemplar(example) {
    if (this instanceof Function) {  // jshint ignore:line
      throw 'use childproof to create create safer functions';
    }
    if (example && example.isExemplar) {
      return example;
    }
    const type = typeof example;
    var test, description = type;
    if (type === 'function') {
      description = 'passes ' + example.toString();
      test = subject => example(subject);
    } else if (type === 'object') {
      if (Array.isArray(example)) {
        if (example.length === 0) {
          description = 'array';
          test = subject => Array.isArray(subject);
        } else {
          return exemplar.array(example[0]);
        }
      } else {
        test = subject => typeof subject === type && !Array.isArray(subject);
      }
    } else {
      test = subject => typeof subject === type;
    }
    test.description = description;
    Object.assign(test, specifiers);
    return test;
  }

  exemplar.args = function(argument_list) {
    if (argument_list.length === 0) {
      return types.noarguments;
    }
    const tests = argument_list.map(exemplar);
    return exemplar(
               args => tests.map((test, idx) => test(args[idx])).reduce(AND),
               '(' + tests.map(test => test.description).join(', ') + ')')
        .describe(`(${tests.map(test => test.description).join(', ')})`);
  };

  exemplar.array = function(test) {
    test = exemplar(test);
    return exemplar(
        args => args.length === 0 || args.map(test).reduce(AND),
        'array of ' + test.description);
  };

  exemplar.heteroArray = function(...args) {
    const tests = [].slice.apply(args).map(exemplar);
    return exemplar(
        args => args.length === 0 ||
            args.map(arg => tests.map(test => test(arg)).reduce(OR))
                .reduce(AND),
        'array of ' + tests.map(test => test.description).join(', '));
  };

  exemplar.regex = function(test_expr, description) {
    const test = exemplar(s => typeof s === 'string' && !!s.match(test_expr));
    test.description = description || 'matches ' + test_expr.toString();
    return test;
  };

  /**
  ### register(type_name, exemplar)

  Adds the exemplar to types (and flags duplicates).

      register(exemplar)

  Registers the exemplar as its description.

  #### Predefined Types

      types.string
      types.stringArray
      types.number
      types.int
      types.cardinal
      types.boolean
      types.object
      types.array
      types.arrayish
      types.function
      types.promise
      types.nonemptyString
      types.undefined
      types.exemplar
      types.rest_method
      types.noarguments
      types.element
      types.nodeList

  ~~~~
  const exemplar = _required_;

  Test(() => exemplar.types.string("hello")).shouldBe(true);
  Test(() => exemplar.types.string("")).shouldBe(true);
  Test(() => exemplar.types.string(17)).shouldBe(false);
  Test(() => exemplar.types.stringArray([])).shouldBe(true);
  Test(() => exemplar.types.stringArray(['hello', 'world'])).shouldBe(true);
  Test(() => exemplar.types.stringArray(['hello', 17,
  'world'])).shouldBe(false);
  Test(() => exemplar.types.int(-17)).shouldBe(true);
  Test(() => exemplar.types.int(1.7)).shouldBe(false);
  Test(() => exemplar.types.cardinal(-17)).shouldBe(false);
  Test(() => exemplar.types.cardinal(0)).shouldBe(true);
  Test(() => exemplar.types.cardinal(17)).shouldBe(true);
  Test(() => exemplar.types.boolean(false)).shouldBe(true);
  Test(() => exemplar.types.boolean(0)).shouldBe(false);
  Test(() => exemplar.types.object({})).shouldBe(true);
  Test(() => exemplar.types.object('')).shouldBe(false);
  Test(() => exemplar.types.array([])).shouldBe(true);
  Test(() => exemplar.types.array({length:0})).shouldBe(false);
  Test(() => exemplar.types.arrayish({length:0})).shouldBe(true);
  Test(() => exemplar.types.arrayish({length:''})).shouldBe(false);
  Test(() => exemplar.types.function(function(){})).shouldBe(true);
  Test(() => exemplar.types.function(()=>{})).shouldBe(true);
  Test(() => exemplar.types.function(Object.assign)).shouldBe(true);
  Test(() => exemplar.types.promise(new Promise(() => {}))).shouldBe(true);
  Test(() => exemplar.types.undefined(undefined)).shouldBe(true);
  Test(() => exemplar.types.exemplar(exemplar.types.int)).shouldBe(true);
  Test(() => exemplar.types.nonemptyString('hello')).shouldBe(true);
  Test(() => exemplar.types.nonemptyString('')).shouldBe(false);
  Test(() => exemplar.types.nonemptyString(null)).shouldBe(false);
  Test(() => exemplar.types.rest_method('info')).shouldBe(true);
  Test(() => exemplar.types.rest_method('pull')).shouldBe(false);
  Test(() => exemplar.types.noarguments({length:0})).shouldBe(true);
  Test(() =>
  exemplar.types.element(document.createElement('div'))).shouldBe(true);
  Test(() =>
  exemplar.types.nodeList(document.querySelectorAll('input'))).shouldBe(true);
  ~~~~
  */

  var types = {
    string: exemplar(''),
    stringArray: exemplar.array(''),
    number: exemplar(0),
    int: exemplar(subject => !(subject % 1)).describe('int'),
    cardinal: exemplar(0).whole().and(subject => subject >= 0),
    boolean: exemplar(true),
    object: exemplar({}).and(exemplar([]).not()).describe('object (NOT array)'),
    array: exemplar([]),
    arrayish: exemplar({})
                  .and(subject => typeof subject.length === 'number')
                  .describe('arrayish (has length)'),
    function:
        exemplar(subject => subject instanceof Function).describe('function'),
    promise: exemplar(new Promise(() => {})),
    nonemptyString: exemplar('').nonempty(),
    undefined: exemplar(undefined),
    exemplar: exemplar(subject => subject.isExemplar).describe('exemplar'),
    rest_method: exemplar.regex(/^(get|post|put|delete|info)$/i, 'REST method'),
    noarguments:
        exemplar(subject => subject.length === 0).describe('no arguments'),
    element: exemplar(subject => subject instanceof HTMLElement),
    nodeList: exemplar(subject => subject instanceof NodeList),
  };

  function register(name, type) {
    if (name.isExemplar) {
      type = name;
      name = type.description;
    }
    if (types[name]) {
      throw `${name} has already been registered`;
    }
    types[name] = type;
  }

  function _childproof(unsafe, expects, returns) {
    if (unsafe.__unsafe) {
      return unsafe;
    }
    if (types.array(expects)) {
      expects = exemplar.args(expects);
    }
    if (typeof expects === 'string') {
      expects = types[expects];
    }
    if (typeof returns === 'string') {
      returns = types[returns] || types.string;
    } else {
      returns = exemplar(returns);
      if (returns.description === 'undefined') {
        returns.describe('no return value');
      }
    }
    const childproofed = function(...args) {
      if (!expects(args)) {
        debugger;  // jshint ignore:line
        throw 'bad arguments, expected ' + expects.description;
      }
      const output = unsafe.apply(this, args);
      if (!returns(output)) {
        debugger;  // jshint ignore:line
        throw 'bad output, expected ' + returns.description;
      }
    };
    childproofed.__unsafe = unsafe;
    return childproofed;
  }

  const childproof =
      _childproof;  //(_childproof,
                    //types.array.or(types.string).or(types.exemplar),
                    //types.exemplar.or(types.string).optional());

  module.exports = {exemplar, register, types, childproof};

}(module));<commit_msg>formatting<commit_after>/**
# Exemplar.js — gradual typing by example
Copyright ©2016-2017 Tonio Loewald

Relatively painless gradual strong typing by example.

### exemplar: create type checkers by example

    exemplar(example_value)

Creates an exemplar that tests for values of the same type as `example_value`.

e.g.

    const numType = exemplar(1)

`numType` will return true for numbers and false for anything other than a
number.

    exemplar(some_function)

Creates an exemplar that behaves like the function, but it's an exemplar.

**Aside**: why use exemplars rather than just boolean test functions?
**exemplars** can be *specified*, combined, and also describe themselves.
They can also be used to drive the `childproof` function.

### Exemplar Methods

    exemplar.args(array)

Creates an exemplar for testing a sequence of arguments for childproofing a
function.
(Use types.noarguments for functions expecting no arguments.)

    exemplar.array(example)

Creates an exemplar that accepts an array of elements matching the example.

    exemplar.heteroArray(example, another_example, ...)

Creates an exemplar that accepts an array of elements matching one of the
provided examples.

    exemplar.regex(regular_expression)

Creates an exemplar that accepts strings that match the regular expression
provided.

    exemplar(...).describe(new_description)

Allows you to specify (override) the description of the method.

    exemplar(...).check(subject)

spams console.error if the subject is rejected

    exemplar(...).test(subject)

throws if the subject is rejected

### Specifiers

Specifiers allow you to tighten up an exemplar to accept very specific things.
E.g.

    exemplar({foo: 13})

creates an exemplar that accepts *any* object. But:

    exemplar({}).properties({foo: 13})

creates an exemplar that only accepts object with a property `foo` that is a
number.

    exemplar(...).optional()

will also accept `undefined`.

    exemplar(...).nullable()

will also accept `null`.

    exemplar(...).nonempty()

must have a numeric, positive length property.

#### Numeric specifiers

    exemplar(...).nonzero()
    exemplar(...).positive()
    exemplar(...).whole()
    exemplar(...).among()

#### Combining specifiers

    exemplar(...).and(exemplar_or_test_function)
    exemplar(...).or(exemplar_or_test_function)
    exemplar(...).not()

#### Object Specifiers

    exemplar(...).properties(object_example)

requires the object to have properties matching the example.

~~~~
const {exemplar} = _required_;
const numType = exemplar(1);
Test(() => numType(-11)).shouldBe(true);
Test(() => numType(Math.PI)).shouldBe(true);
Test(() => numType('3.14')).shouldBe(false);
Test(() => numType(true)).shouldBe(false);

const objType = exemplar({});
Test(() => objType({})).shouldBe(true);
Test(() => objType('test')).shouldBe(false);
Test(() => objType([])).shouldBe(false);

const geoType = objType.properties({name: 'fred', position:{x: 10, y: 100}});
Test(() => geoType({})).shouldBe(false);
Test(() => geoType({name: 'anne', position: {}})).shouldBe(true);

const geoType2 = objType.properties({name: 'fred',
position:objType.properties({x: 10, y: 100})});
Test(() => geoType2({name: 'anne', position: {}})).shouldBe(false);
Test(() => geoType2({name: 'anne', position: {x: -5, y: 33}})).shouldBe(true);
~~~~

### Childproof Functions

    childproof(function, expects, returns)

Produces a *childproofed* function that will throw if passed arguments other
that it expects
or if it returns a value other than expected (returns is optional — if omitted,
the function
is expected *not* to return a value).

expects or returns are treated as type references if a string if passed, and
treated as
*/
/* global module, console */
(function(module) {

  'use strict';

  const AND = (a, b) => a && b;
  const OR = (a, b) => a || b;

  const specifiers = {
    isExemplar: true,
    optional: function() {
      const test = this;
      return exemplar(subject => test(subject) || types.undefined(subject))
          .describe(test.description + ' (optional)');
    },
    nullable: function() {
      const test = this;
      return exemplar(subject => test(subject) || types.null(subject))
          .describe(test.description + ' OR null');
    },
    nonempty: function() {
      const test = this;
      return exemplar(
                 subject => test(subject) && types.number(subject.length) &&
                     subject.length > 0)
          .describe('non empty ' + test.description);
    },
    nonzero: function() {
      const test = this;
      return exemplar(subject => test(subject) && subject !== 0)
          .describe('non-zero ' + test.description);
    },
    positive: function() {
      const test = this;
      return exemplar(subject => test(subject) && subject > 0)
          .describe(test.descriotion = ' (>0)');
    },
    whole: function() {
      const test = this;
      return exemplar(subject => test(subject) && types.int(subject));
    },
    among: function(options) {
      const test = this;
      return exemplar(subject => test(subject) && options.indexOf(subject) > -1)
          .describe(`(${options.join('|')})`);
    },
    and: function(otherTest) {
      const test = this;
      return exemplar(subject => test(subject) && otherTest(subject))
          .describe(`${test.description} and ${otherTest.description}`);
    },
    or: function(otherTest) {
      const test = this;
      return exemplar(subject => test(subject) || otherTest(subject))
          .describe(`${test.description} or ${otherTest.description}`);
    },
    not: function() {
      const test = this;
      return exemplar(subject => !test(subject))
          .describe('not ' + test.description);
    },
    properties: function(object) {
      const test = this;
      const keys = Object.keys(object);
      const test_map = {};
      keys.forEach(key => test_map[key] = exemplar(object[key]));
      const checkProps = subject =>
          keys.map(key => test_map[key](subject[key])).reduce(AND);
      const description = test.description + ' with properties ' +
          keys.map(key => `key (${test_map[key].description})`).join(', ');
      return exemplar(subject => test(subject) && checkProps(subject))
          .describe(description);
    },
    describe: function(description) {
      this.description = description;
      return this;
    },
    check: subject => {
      if (!this(subject)) {
        console.error('expected', this.description);
      }
    },
    test: subject => {
      if (!this(subject)) {
        throw 'expected ' + this.description;
      }
    },
  };
  Object.freeze(specifiers);

  function exemplar(example) {
    if (this instanceof Function) {  // jshint ignore:line
      throw 'use childproof to create create safer functions';
    }
    if (example && example.isExemplar) {
      return example;
    }
    const type = typeof example;
    var test, description = type;
    if (type === 'function') {
      description = 'passes ' + example.toString();
      test = subject => example(subject);
    } else if (type === 'object') {
      if (Array.isArray(example)) {
        if (example.length === 0) {
          description = 'array';
          test = subject => Array.isArray(subject);
        } else {
          return exemplar.array(example[0]);
        }
      } else {
        test = subject => typeof subject === type && !Array.isArray(subject);
      }
    } else {
      test = subject => typeof subject === type;
    }
    test.description = description;
    Object.assign(test, specifiers);
    return test;
  }

  exemplar.args = function(argument_list) {
    if (argument_list.length === 0) {
      return types.noarguments;
    }
    const tests = argument_list.map(exemplar);
    return exemplar(
               args => tests.map((test, idx) => test(args[idx])).reduce(AND),
               '(' + tests.map(test => test.description).join(', ') + ')')
        .describe(`(${tests.map(test => test.description).join(', ')})`);
  };

  exemplar.array = function(test) {
    test = exemplar(test);
    return exemplar(
        args => args.length === 0 || args.map(test).reduce(AND),
        'array of ' + test.description);
  };

  exemplar.heteroArray = function(...args) {
    const tests = [].slice.apply(args).map(exemplar);
    return exemplar(
        args => args.length === 0 ||
            args.map(arg => tests.map(test => test(arg)).reduce(OR))
                .reduce(AND),
        'array of ' + tests.map(test => test.description).join(', '));
  };

  exemplar.regex = function(test_expr, description) {
    const test = exemplar(s => typeof s === 'string' && !!s.match(test_expr));
    test.description = description || 'matches ' + test_expr.toString();
    return test;
  };

/**
### register(type_name, exemplar)

Adds the exemplar to types (and flags duplicates).

    register(exemplar)

Registers the exemplar as its description.

#### Predefined Types

    types.string
    types.stringArray
    types.number
    types.int
    types.cardinal
    types.boolean
    types.object
    types.array
    types.arrayish
    types.function
    types.promise
    types.nonemptyString
    types.undefined
    types.exemplar
    types.rest_method
    types.noarguments
    types.element
    types.nodeList

~~~~
const exemplar = _required_;

Test(() => exemplar.types.string("hello")).shouldBe(true);
Test(() => exemplar.types.string("")).shouldBe(true);
Test(() => exemplar.types.string(17)).shouldBe(false);
Test(() => exemplar.types.stringArray([])).shouldBe(true);
Test(() => exemplar.types.stringArray(['hello', 'world'])).shouldBe(true);
Test(() => exemplar.types.stringArray(['hello', 17,
'world'])).shouldBe(false);
Test(() => exemplar.types.int(-17)).shouldBe(true);
Test(() => exemplar.types.int(1.7)).shouldBe(false);
Test(() => exemplar.types.cardinal(-17)).shouldBe(false);
Test(() => exemplar.types.cardinal(0)).shouldBe(true);
Test(() => exemplar.types.cardinal(17)).shouldBe(true);
Test(() => exemplar.types.boolean(false)).shouldBe(true);
Test(() => exemplar.types.boolean(0)).shouldBe(false);
Test(() => exemplar.types.object({})).shouldBe(true);
Test(() => exemplar.types.object('')).shouldBe(false);
Test(() => exemplar.types.array([])).shouldBe(true);
Test(() => exemplar.types.array({length:0})).shouldBe(false);
Test(() => exemplar.types.arrayish({length:0})).shouldBe(true);
Test(() => exemplar.types.arrayish({length:''})).shouldBe(false);
Test(() => exemplar.types.function(function(){})).shouldBe(true);
Test(() => exemplar.types.function(()=>{})).shouldBe(true);
Test(() => exemplar.types.function(Object.assign)).shouldBe(true);
Test(() => exemplar.types.promise(new Promise(() => {}))).shouldBe(true);
Test(() => exemplar.types.undefined(undefined)).shouldBe(true);
Test(() => exemplar.types.exemplar(exemplar.types.int)).shouldBe(true);
Test(() => exemplar.types.nonemptyString('hello')).shouldBe(true);
Test(() => exemplar.types.nonemptyString('')).shouldBe(false);
Test(() => exemplar.types.nonemptyString(null)).shouldBe(false);
Test(() => exemplar.types.rest_method('info')).shouldBe(true);
Test(() => exemplar.types.rest_method('pull')).shouldBe(false);
Test(() => exemplar.types.noarguments({length:0})).shouldBe(true);
Test(() =>
exemplar.types.element(document.createElement('div'))).shouldBe(true);
Test(() =>
exemplar.types.nodeList(document.querySelectorAll('input'))).shouldBe(true);
~~~~
*/

  var types = {
    string: exemplar(''),
    stringArray: exemplar.array(''),
    number: exemplar(0),
    int: exemplar(subject => !(subject % 1)).describe('int'),
    cardinal: exemplar(0).whole().and(subject => subject >= 0),
    boolean: exemplar(true),
    object: exemplar({}).and(exemplar([]).not()).describe('object (NOT array)'),
    array: exemplar([]),
    arrayish: exemplar({})
                  .and(subject => typeof subject.length === 'number')
                  .describe('arrayish (has length)'),
    function:
        exemplar(subject => subject instanceof Function).describe('function'),
    promise: exemplar(new Promise(() => {})),
    nonemptyString: exemplar('').nonempty(),
    undefined: exemplar(undefined),
    exemplar: exemplar(subject => subject.isExemplar).describe('exemplar'),
    rest_method: exemplar.regex(/^(get|post|put|delete|info)$/i, 'REST method'),
    noarguments:
        exemplar(subject => subject.length === 0).describe('no arguments'),
    element: exemplar(subject => subject instanceof HTMLElement),
    nodeList: exemplar(subject => subject instanceof NodeList),
  };

  function register(name, type) {
    if (name.isExemplar) {
      type = name;
      name = type.description;
    }
    if (types[name]) {
      throw `${name} has already been registered`;
    }
    types[name] = type;
  }

  function _childproof(unsafe, expects, returns) {
    if (unsafe.__unsafe) {
      return unsafe;
    }
    if (types.array(expects)) {
      expects = exemplar.args(expects);
    }
    if (typeof expects === 'string') {
      expects = types[expects];
    }
    if (typeof returns === 'string') {
      returns = types[returns] || types.string;
    } else {
      returns = exemplar(returns);
      if (returns.description === 'undefined') {
        returns.describe('no return value');
      }
    }
    const childproofed = function(...args) {
      if (!expects(args)) {
        debugger;  // jshint ignore:line
        throw 'bad arguments, expected ' + expects.description;
      }
      const output = unsafe.apply(this, args);
      if (!returns(output)) {
        debugger;  // jshint ignore:line
        throw 'bad output, expected ' + returns.description;
      }
    };
    childproofed.__unsafe = unsafe;
    return childproofed;
  }

  const childproof =
      _childproof;  //(_childproof,
                    //types.array.or(types.string).or(types.exemplar),
                    //types.exemplar.or(types.string).optional());

  module.exports = {exemplar, register, types, childproof};

}(module));
<|endoftext|>


<commit_before>/*! Menu - v0.1.3 - 2016-10-19
* https://github.com/filamentgroup/menu
* Copyright (c) 2016 Scott Jehl; Licensed MIT */
window.jQuery = window.jQuery || window.shoestring;

(function( $, w ) {
	"use strict";

	var componentName = "Menu",
		at = {
			ariaHidden: "aria-hidden"
		},
		selectClass = "menu-selected",
		focusables = "a,input,[tabindex]";

	var menu = function( element ){
		if( !element ){
			throw new Error( "Element required to initialize object" );
		}
		this.element = element;
		this.$element = $( element );
		this.opened = true;
	};

	menu.prototype.fill = function( items, selectedText ) {
		var html = "";

		$.each( items, function( i, item ){
			html += "
" + item + "
";
		});

		this.$element.find( "ol,ul" ).html( html );
	};

	menu.prototype.moveSelected = function( placement, focus ){
		var $items = this.$element.find( "li" ),
			$selected = $items.filter( "." + selectClass ),
			$nextSelected;

		if( !$selected.length || placement === "start" ){
			$nextSelected = $items.eq( 0 );
		}
		else if( placement === "next" ){
			$nextSelected = $selected.next();
			if( !$nextSelected.length ){
				$nextSelected = $items.eq( 0 );
			}
		}
		else {
			$nextSelected = $selected.prev();
			if( !$nextSelected.length ){
				$nextSelected = $items.eq( $items.length - 1 );
			}
		}
		$selected.removeClass( selectClass );
		$nextSelected.addClass( selectClass );

		if( focus || $( w.document.activeElement ).closest( $selected ).length ){
			if( $nextSelected.is( focusables ) ){
				$nextSelected[ 0 ].focus();
			}
			else{
				var $focusChild = $nextSelected.find( focusables );
				if( $focusChild.length ){
					$focusChild[ 0 ].focus();
				}
			}
		}
	};

	menu.prototype.getSelectedElement = function(){
		return this.$element.find( "li." + selectClass );
	};

	menu.prototype.selectActive = function(){
		var trigger = this.$element.data( componentName + "-trigger" );
		var $selected = this.getSelectedElement();

		if( trigger && $( trigger ).is( "input" ) ){
			trigger.value = $selected.text();
		}
		$selected.trigger( componentName + ":select" );
		this.close();
		return $selected.text();
	};

	menu.prototype.keycodes = {
		38 : function(e) {
			this.moveSelected( "prev" );
			e.preventDefault();
		},

		40 : function(e){
			this.moveSelected( "next" );
			e.preventDefault();
		},

		13 : function(){
			// return the selected value
			return this.selectActive();
		},

		9 : function(e){
			this.moveSelected( e.shiftKey ? "prev" : "next" );
			e.preventDefault();
		},

		27 : function(){
			this.close();
		}
	};

	menu.prototype.keyDown = function( e ){
		var fn = this.keycodes[e.keyCode] || function(){};
		return fn.call( this, e );
	};

	menu.prototype._bindKeyHandling = function(){
		var self = this;
		this.$element
			.bind( "keydown", function( e ){
				self.keyDown( e );
			} )
			.bind( "mouseover", function( e ){
				self.$element.find( "." + selectClass ).removeClass( selectClass );
				$( e.target ).closest( "li" ).addClass( selectClass );
			})
			.bind( "mouseleave", function( e ){
				$( e.target ).closest( "li" ).removeClass( selectClass );
			})
			.bind( "click", function(){
				self.selectActive();
			});
	};

	menu.prototype.open = function( trigger, sendFocus ){
		if( this.opened ){
			return;
		}
		this.$element.attr( at.ariaHidden, false );

		this.$element.data( componentName + "-trigger", trigger );
		this.opened = true;
		this.moveSelected( "start", sendFocus );
		this.$element.trigger( componentName + ":open" );
	};

	menu.prototype.close = function(){
		if( !this.opened ){
			return;
		}
		this.$element.attr( at.ariaHidden, true );
		this.opened = false;
		var $trigger = this.$element.data( componentName + "-trigger" );
		if( $trigger ){
			$trigger.focus();
		}
		this.$element.trigger( componentName + ":close" );
	};

	menu.prototype.toggle = function( trigger, sendFocus ){
		this[ this.opened ? "close" : "open" ]( trigger, sendFocus );
	};

	menu.prototype.init = function(){
		// prevent re-init
		if( this.$element.data( componentName ) ) {
			return;
		}
		this.$element.data( componentName, this );

		this.$element.attr( "role", "menu" );
		this.close();
		var self = this;

		// close on any click, even on the menu
		// NOTE we can't close on `mouseup` in case there's overflow scrolling
		// because the `mouseup` event is fired when using the scrollbar
		$( document ).bind( "mouseup", function(){
			self.close();
		});

		this._bindKeyHandling();

		this.$element.trigger( componentName + ":init" );
	};

	menu.prototype.isOpen = function(){
		return this.opened;
	};

	(w.componentNamespace = w.componentNamespace || w)[ componentName ] = menu;
}( jQuery, this ));
<commit_msg>actually update the menu dep<commit_after>/*! Menu - v0.1.3 - 2016-10-19
* https://github.com/filamentgroup/menu
* Copyright (c) 2016 Scott Jehl; Licensed MIT */
window.jQuery = window.jQuery || window.shoestring;

(function( $, w ) {
	"use strict";

	var componentName = "Menu",
		at = {
			ariaHidden: "aria-hidden"
		},
		selectClass = "menu-selected",
		focusables = "a,input,[tabindex]";

	var menu = function( element ){
		if( !element ){
			throw new Error( "Element required to initialize object" );
		}
		this.element = element;
		this.$element = $( element );
		this.opened = true;
	};

	menu.prototype.fill = function( items, selectedText ) {
		var html = "";

		$.each( items, function( i, item ){
			html += "
" + item + "
";
		});

		this.$element.find( "ol,ul" ).html( html );
	};

	menu.prototype.moveSelected = function( placement, focus ){
		var $items = this.$element.find( "li" ),
			$selected = $items.filter( "." + selectClass ),
			$nextSelected;

		if( !$selected.length || placement === "start" ){
			$nextSelected = $items.eq( 0 );
		}
		else if( placement === "next" ){
			$nextSelected = $selected.next();
			if( !$nextSelected.length ){
				$nextSelected = $items.eq( 0 );
			}
		}
		else {
			$nextSelected = $selected.prev();
			if( !$nextSelected.length ){
				$nextSelected = $items.eq( $items.length - 1 );
			}
		}
		$selected.removeClass( selectClass );
		$nextSelected.addClass( selectClass );

		if( focus || $( w.document.activeElement ).closest( $selected ).length ){
			if( $nextSelected.is( focusables ) ){
				$nextSelected[ 0 ].focus();
			}
			else{
				var $focusChild = $nextSelected.find( focusables );
				if( $focusChild.length ){
					$focusChild[ 0 ].focus();
				}
			}
		}
	};

	menu.prototype.getSelectedElement = function(){
		return this.$element.find( "li." + selectClass );
	};

	menu.prototype.selectActive = function(){
		var trigger = this.$element.data( componentName + "-trigger" );
		var $selected = this.getSelectedElement();

		if( trigger && $( trigger ).is( "input" ) ){
			trigger.value = $selected.text();
		}
		$selected.trigger( componentName + ":select" );
		this.close();
		return $selected.text();
	};

	menu.prototype.keycodes = {
		38 : function(e) {
			this.moveSelected( "prev" );
			e.preventDefault();
		},

		40 : function(e){
			this.moveSelected( "next" );
			e.preventDefault();
		},

		13 : function(){
			// return the selected value
			return this.selectActive();
		},

		9 : function(e){
			this.moveSelected( e.shiftKey ? "prev" : "next" );
			e.preventDefault();
		},

		27 : function(){
			this.close();
		}
	};

	menu.prototype.keyDown = function( e ){
		var fn = this.keycodes[e.keyCode] || function(){};
		return fn.call( this, e );
	};

	menu.prototype._bindKeyHandling = function(){
		var self = this;
		this.$element
			.bind( "keydown", function( e ){
				self.keyDown( e );
			} )
			.bind( "mouseover", function( e ){
				self.$element.find( "." + selectClass ).removeClass( selectClass );
				$( e.target ).closest( "li" ).addClass( selectClass );
			})
			.bind( "mouseleave", function( e ){
				$( e.target ).closest( "li" ).removeClass( selectClass );
			})
			.bind( "click", function(){
				self.selectActive();
			});
	};

	menu.prototype.open = function( trigger, sendFocus ){
		if( this.opened ){
			return;
		}
		this.$element.attr( at.ariaHidden, false );

		this.$element.data( componentName + "-trigger", trigger );
		this.opened = true;
		this.moveSelected( "start", sendFocus );
		this.$element.trigger( componentName + ":open" );
	};

	menu.prototype.close = function(){
		if( !this.opened ){
			return;
		}
		this.$element.attr( at.ariaHidden, true );
		this.opened = false;
		var $trigger = this.$element.data( componentName + "-trigger" );
		if( $trigger ){
			$trigger.focus();
		}
		this.$element.trigger( componentName + ":close" );
	};

	menu.prototype.toggle = function( trigger, sendFocus ){
		this[ this.opened ? "close" : "open" ]( trigger, sendFocus );
	};

	menu.prototype.init = function(){
		// prevent re-init
		if( this.$element.data( componentName ) ) {
			return;
		}
		this.$element.data( componentName, this );

		this.$element.attr( "role", "menu" );
		this.close();
		var self = this;

		$( document ).bind( "mouseup", function(event){
			// only close the menu if the click is outside the menu element
			if( ! $(event.target).closest( self.$element[0] ).length ){
				self.close();
			}
		});

		this._bindKeyHandling();

		this.$element.trigger( componentName + ":init" );
	};

	menu.prototype.isOpen = function(){
		return this.opened;
	};

	(w.componentNamespace = w.componentNamespace || w)[ componentName ] = menu;
}( jQuery, this ));
<|endoftext|>


<commit_before>var path = require("path");
var fs = require("fs");
var cp = require("child_process");
var prepareForBuild = require("./prepareForBuild");
var exec = require("../utils/execPromise");

var fromRegistry;

try {
  fs.statSync(path.join(__dirname, "..", "include"));
  fs.statSync(path.join(__dirname, "..", "src"));
  fs.statSync(path.join(__dirname, "..", "dist"));
  fromRegistry = true;
}
catch(e) {
  fromRegistry = false;
}

if (!fromRegistry) {
  console.info("[nodegit] Local install, no fetching allowed.");
  return prepareAndBuild();
}
if (process.env.BUILD_DEBUG) {
  console.info("[nodegit] Doing a debug build, no fetching allowed.");
  return prepareAndBuild();
}
if (process.env.BUILD_ONLY) {
  console.info("[nodegit] BUILD_ONLY is set to true, no fetching allowed.");
  return prepareAndBuild();
}

return installPrebuilt();

function installPrebuilt() {
  console.info("[nodegit] Fetching binary from S3.");
  var npg = pathForTool("node-pre-gyp");
  return exec("\""+ npg + "\" install --fallback-to-build=false")
    .then(
      function() {
        console.info("[nodegit] Completed installation successfully.");
      },
      function(err) {
        console.info("[nodegit] Failed to install prebuilt binary:");
        console.error(err);
        console.info("[nodegit] Building manually. (You'll be here a while.)");
        return prepareAndBuild();
      }
    );
}

function pathForTool(name) {
  var toolPath = path.resolve(".", "node_modules", ".bin", name);
  if (process.platform == "win32") {
    toolPath += ".cmd";
  }
  return toolPath;
}

function prepareAndBuild() {
  console.info("[nodegit] Regenerating and configuring code");
  return prepareForBuild()
    .then(function() {
      return build();
    })
    .then(function() {
      return transpileJavascript();
    });
}

function transpileJavascript() {
  var cmd = pathForTool("babel");
  var args = [
    "--presets",
    "es2015",
    "-d",
    "./dist",
    "./lib"
  ];
  var opts = {
    cwd: ".",
    maxBuffer: Number.MAX_VALUE,
    env: process.env,
    stdio: "inherit"
  };
  var home = process.platform == "win32" ?
              process.env.USERPROFILE : process.env.HOME;

  opts.env.HOME = path.join(home, ".nodegit-gyp");

  return new Promise(function(resolve, reject) {
    var child = cp.spawn(cmd, args, opts);
    child.on("close", function(code) {
      if (code) {
        reject(code);
        process.exitCode = 13;
      }
      else {
        resolve();
      }
    });
  });
}

function build() {
  console.info("[nodegit] Everything is ready to go, attempting compilation");

  var electronVersion = process.env.ELECTRON_VERSION;
  var nwjsVersion = process.env.NWJS_VERSION;
  var opts = {
    cwd: ".",
    maxBuffer: Number.MAX_VALUE,
    env: process.env,
    stdio: "inherit"
  };

  var builder = "node-gyp";
  var debug = (process.env.BUILD_DEBUG ? "--debug" : "");
  var target = "";
  var arch = (process.env.TARGET_ARCH ?
	"--arch=" + process.env.TARGET_ARCH : "");
  var distUrl = "";
  var runtime = "";

  process.argv.forEach(function(arg) {
    if (~arg.indexOf("electronVersion")) {
      electronVersion = arg.split("=")[1].trim();
    }
    else if (~arg.indexOf("nsjwVersion")) {
      nwjsVersion = arg.split("=")[1].trim();
    }
  });

  if (electronVersion) {
    target = "--target=" + electronVersion;
    distUrl = "--dist-url=https://atom.io/download/atom-shell";
    runtime = "--runtime=electron";
  }
  else if (nwjsVersion) {
    builder = "nw-gyp";
    target = "--target=" + nwjsVersion;
    runtime = "--runtime=node-webkit";
  }

  var home = process.platform == "win32" ?
              process.env.USERPROFILE : process.env.HOME;

  opts.env.HOME = path.join(home, ".nodegit-gyp");

  var cmd = pathForTool(builder);
  var args = [
    "rebuild",
    debug,
    target,
    arch,
    distUrl,
    runtime
  ]
  .filter(function(arg) {
    return arg;
  });

  return new Promise(function(resolve, reject) {
    var child = cp.spawn(cmd, args, opts);
    child.on("close", function(code) {
      console.log(code);
      if (code) {
        reject(code);
        process.exitCode = 13;
      }
      else {
        resolve();
      }
    });
  });
}
<commit_msg>Fix install script error getting swallowed<commit_after>var path = require("path");
var fs = require("fs");
var cp = require("child_process");
var prepareForBuild = require("./prepareForBuild");
var exec = require("../utils/execPromise");

module.exports = function install() {
  var fromRegistry;

  try {
    fs.statSync(path.join(__dirname, "..", "include"));
    fs.statSync(path.join(__dirname, "..", "src"));
    fs.statSync(path.join(__dirname, "..", "dist"));
    fromRegistry = true;
  }
  catch(e) {
    fromRegistry = false;
  }

  if (!fromRegistry) {
    console.info("[nodegit] Local install, no fetching allowed.");
    return prepareAndBuild();
  }
  if (process.env.BUILD_DEBUG) {
    console.info("[nodegit] Doing a debug build, no fetching allowed.");
    return prepareAndBuild();
  }
  if (process.env.BUILD_ONLY) {
    console.info("[nodegit] BUILD_ONLY is set to true, no fetching allowed.");
    return prepareAndBuild();
  }

  return installPrebuilt();

  function installPrebuilt() {
    console.info("[nodegit] Fetching binary from S3.");
    var npg = pathForTool("node-pre-gyp");
    return exec("\""+ npg + "\" install --fallback-to-build=false")
    .then(
      function() {
        console.info("[nodegit] Completed installation successfully.");
      },
      function(err) {
        console.info("[nodegit] Failed to install prebuilt binary:");
        console.error(err);
        console.info("[nodegit] Building manually. (You'll be here a while.)");
        return prepareAndBuild();
      }
    );
  }

  function pathForTool(name) {
    var toolPath = path.resolve(".", "node_modules", ".bin", name);
    if (process.platform == "win32") {
      toolPath += ".cmd";
    }
    return toolPath;
  }

  function prepareAndBuild() {
    console.info("[nodegit] Regenerating and configuring code");
    return prepareForBuild()
    .then(function() {
      return build();
    })
    .then(function() {
      return transpileJavascript();
    });
  }

  function transpileJavascript() {
    var cmd = pathForTool("babel");
    var args = [
      "--presets",
      "es2015",
      "-d",
      "./dist",
      "./lib"
    ];
    var opts = {
      cwd: ".",
      maxBuffer: Number.MAX_VALUE,
      env: process.env,
      stdio: "inherit"
    };
    var home = process.platform == "win32" ?
    process.env.USERPROFILE : process.env.HOME;

    opts.env.HOME = path.join(home, ".nodegit-gyp");

    return new Promise(function(resolve, reject) {
      var child = cp.spawn(cmd, args, opts);
      child.on("close", function(code) {
        if (code) {
          reject(code);
          process.exitCode = 13;
        }
        else {
          resolve();
        }
      });
    });
  }

  function build() {
    console.info("[nodegit] Everything is ready to go, attempting compilation");

    var electronVersion = process.env.ELECTRON_VERSION;
    var nwjsVersion = process.env.NWJS_VERSION;
    var opts = {
      cwd: ".",
      maxBuffer: Number.MAX_VALUE,
      env: process.env,
      stdio: "inherit"
    };

    var builder = "node-gyp";
    var debug = (process.env.BUILD_DEBUG ? "--debug" : "");
    var target = "";
    var arch = (process.env.TARGET_ARCH ?
      "--arch=" + process.env.TARGET_ARCH : "");
      var distUrl = "";
      var runtime = "";

      process.argv.forEach(function(arg) {
        if (~arg.indexOf("electronVersion")) {
          electronVersion = arg.split("=")[1].trim();
        }
        else if (~arg.indexOf("nsjwVersion")) {
          nwjsVersion = arg.split("=")[1].trim();
        }
      });

      if (electronVersion) {
        target = "--target=" + electronVersion;
        distUrl = "--dist-url=https://atom.io/download/atom-shell";
        runtime = "--runtime=electron";
      }
      else if (nwjsVersion) {
        builder = "nw-gyp";
        target = "--target=" + nwjsVersion;
        runtime = "--runtime=node-webkit";
      }

      var home = process.platform == "win32" ?
      process.env.USERPROFILE : process.env.HOME;

      opts.env.HOME = path.join(home, ".nodegit-gyp");

      var cmd = pathForTool(builder);
      var args = [
        "rebuild",
        debug,
        target,
        arch,
        distUrl,
        runtime
      ]
      .filter(function(arg) {
        return arg;
      });

      return new Promise(function(resolve, reject) {
        var child = cp.spawn(cmd, args, opts);
        child.on("close", function(code) {
          console.log(code);
          if (code) {
            reject(code);
            process.exitCode = 13;
          }
          else {
            resolve();
          }
        });
      });
    }
};

// Called on the command line
if (require.main === module) {
  module
    .exports()
    .catch(function(err) {
      console.error(err);
      return -1;
    });
}
<|endoftext|>


<commit_before>let Cleverbot = require('cleverbot-node'),
    onee = new Cleverbot,
    decode = require('entities');

Cleverbot.prepare(() => {});

module.exports = {
    usage: "Chat with this bot using the Cleverbot API\n`chat [text]` or `@BotMention [text]`",
    delete: false,
    cooldown: 2,
    process: msg => {
        return new Promise(resolve => {
            let text = (msg.cleanContent.split(' ').length > 1) ? msg.cleanContent.substring(msg.cleanContent.indexOf(' ') + 1).replace('@', '') : 'Hi';
            msg.channel.sendTyping();
            Cleverbot.prepare(() => {
                onee.write(text, response => {
                    if (!response.message) {
                        resolve({
                            message: "Cleverbot is currently reseting. Please try again in a moment",
                            delete: true
                        });
                        delete require.cache[require.resolve('cleverbot-node')];
                        Cleverbot = require('cleverbot-node');
                        onee = new Cleverbot();
                        console.log("Cleverbot was reset because nothing was returned");
                    } else {
                        response = unicodeToChar(response.message.replace(/<br \/>/g, " ").replace(/\r?\n|\r/g, "\n").replace(/\[(i|\/i)\]/g, "*").replace(/\[(b|\/b)\]/g, "**").replace(/\|/g, "\\u"))
                        resolve({
                            message: "🗨 - " + decode.decodeHTML(response)
                        })
                    }
                });
            });
        });
    }
}

function unicodeToChar(text) {
    return text.replace(/\\u[\dA-F]{4}/gi,
        function(match) {
            return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16));
        });
}<commit_msg>Added aliases to chat<commit_after>let Cleverbot = require('cleverbot-node'),
    onee = new Cleverbot,
    decode = require('entities');

Cleverbot.prepare(() => {});

module.exports = {
    usage: "Chat with this bot using the Cleverbot API\n`chat [text]` or `@BotMention [text]`",
    aliases: ['clever', 'cleverbot', 'talk'],
    delete: false,
    cooldown: 2,
    process: msg => {
        return new Promise(resolve => {
            let text = (msg.cleanContent.split(' ').length > 1) ? msg.cleanContent.substring(msg.cleanContent.indexOf(' ') + 1).replace('@', '') : 'Hi';
            msg.channel.sendTyping();
            Cleverbot.prepare(() => {
                onee.write(text, response => {
                    if (!response.message) {
                        resolve({
                            message: "Cleverbot is currently reseting. Please try again in a moment",
                            delete: true
                        });
                        delete require.cache[require.resolve('cleverbot-node')];
                        Cleverbot = require('cleverbot-node');
                        onee = new Cleverbot();
                        console.log("Cleverbot was reset because nothing was returned");
                    } else {
                        response = unicodeToChar(response.message.replace(/<br \/>/g, " ").replace(/\r?\n|\r/g, "\n").replace(/\[(i|\/i)\]/g, "*").replace(/\[(b|\/b)\]/g, "**").replace(/\|/g, "\\u"))
                        resolve({
                            message: "🗨 - " + decode.decodeHTML(response)
                        })
                    }
                });
            });
        });
    }
}

function unicodeToChar(text) {
    return text.replace(/\\u[\dA-F]{4}/gi,
        function(match) {
            return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16));
        });
}<|endoftext|>


<commit_before>module.exports = Cmds.addCommand({
    cmds: [';raidmode', ';start raidmode', ';enable raidmode'],

    requires: {
        guild: true,
        loud: false,
    },

    desc: 'Activate raid mode',

    args: '',

    example: '',

    // /////////////////////////////////////////////////////////////////////////////////////////

    func: (cmd, args, msgObj, speaker, channel, guild) => {
        if (index.raidMode[guild.id]) {
            Util.log('Raid mode is already active');
            Util.print(channel, 'Raid mode is already active');
            return;
        }

        index.activateRaidMode(guild, channel);
    },
});
<commit_msg>Minor fix<commit_after>module.exports = Cmds.addCommand({
    cmds: [';raidmode', ';start raidmode', ';enable raidmode'],

    requires: {
        guild: true,
        loud: false,
    },

    desc: 'Activate raid mode',

    args: '',

    example: '',

    // /////////////////////////////////////////////////////////////////////////////////////////

    func: (cmd, args, msgObj, speaker, channel, guild) => {
        if (index.raidMode[guild.id]) {
            Util.log('Raid mode is already active');
            Util.print(channel, 'Raid mode is already active');
            return;
        }

        const joinStamp = +new Date();

        index.recentMembers = index.recentMembers.filter(memberData => joinStamp - memberData.joinStamp < index.newMemberTime);
        index.recentMembers2 = index.recentMembers2.filter(memberData => joinStamp - memberData.joinStamp < index.newMemberTime2);
        index.activateRaidMode(guild, channel);
    },
});
<|endoftext|>


<commit_before>/***** BEGIN LICENSE BLOCK ***** {{{
 Copyright © 2008-2009 by Kris Maglione <maglione.k at Gmail>
 Distributable under the terms of the MIT license, which allows
 for sublicensing under any compatible license, including the MPL,
 GPL, and MPL. Anyone who changes this file is welcome to relicense
 it under any or all of those licenseses.
}}} ***** END LICENSE BLOCK *****/

/** @scope modules */

const template = {
    add: function add(a, b) a + b,
    join: function join(c) function (a, b) a + c + b,

    map: function map(iter, fn, sep, interruptable)
    {
        if (iter.length) // FIXME: Kludge?
            iter = util.Array.iterator(iter);
        let ret = <></>;
        let n = 0;
        for each (let i in Iterator(iter))
        {
            let val = fn(i);
            if (val == undefined)
                continue;
            if (sep && n++)
                ret += sep;
            if (interruptable && n % interruptable == 0)
                liberator.threadYield(true, true);
            ret += val;
        }
        return ret;
    },

    maybeXML: function maybeXML(xml)
    {
        if (typeof xml == "xml")
            return xml;
        try
        {
            return new XMLList(xml);
        }
        catch (e) {}
        return <>{xml}</>;
    },

    completionRow: function completionRow(item, class)
    {
        if (typeof icon == "function")
            icon = icon();

        if (class)
        {
            var text = item[0] || "";
            var desc = item[1] || "";
        }
        else
        {
            var text = this.process[0].call(this, item, item.text);
            var desc = this.process[1].call(this, item, item.description);
        }

        // <e4x>
        return

{text}

{desc}

;
        // </e4x>
    },

    bookmarkDescription: function (item, text)
    <>
        {text}
        {
            !(item.extra && item.extra.length) ? "" :

                ({
                    template.map(item.extra, function (e)
                    <>{e[0]}: {e[1]}</>,
                    <> </>/* Non-breaking space */)
                })

        }
    </>,

    icon: function (item, text)
    {
        return <>{item.icon ?  : <></>}{text}</>
    },

    filter: function (str) {str},

    gradient: function (left, right)

                    { template.map(util.range(0, 100), function (i)
                      ) }

,

    // if "processStrings" is true, any passed strings will be surrounded by " and
    // any line breaks are displayed as \n
    highlight: function highlight(arg, processStrings, clip)
    {
        // some objects like window.JSON or getBrowsers()._browsers need the try/catch
        try
        {
            let str = clip ? util.clip(String(arg), clip) : String(arg);
            switch (arg == null ? "undefined" : typeof arg)
            {
                case "number":
                    return {str};
                case "string":
                    if (processStrings)
                        str = str.quote();
                    return {str};
                case "boolean":
                    return {str};
                case "function":
                    // Vim generally doesn't like /foo*/, because */ looks like a comment terminator.
                    // Using /foo*(:?)/ instead.
                    if (processStrings)
                        return {str.replace(/\{(.|\n)*(?:)/g, "{ ... }")};
                    return <>{arg}</>;
                case "undefined":
                    return {arg};
                case "object":
                    // for java packages value.toString() would crash so badly
                    // that we cannot even try/catch it
                    if (/^\[JavaPackage.*\]$/.test(arg))
                        return <>[JavaPackage]</>;
                    if (processStrings && false)
                        str = template.highlightFilter(str, "\n", function () ^J);
                    return {str};
                case "xml":
                    return arg;
                default:
                    return <![CDATA[<unknown type>]]>;
            }
        }
        catch (e)
        {
            return<![CDATA[<unknown>]]>;
        }
    },

    highlightFilter: function highlightFilter(str, filter, highlight)
    {
        return this.highlightSubstrings(str, (function ()
        {
            if (filter.length == 0)
                return;
            let lcstr = String.toLowerCase(str);
            let lcfilter = filter.toLowerCase();
            let start = 0;
            while ((start = lcstr.indexOf(lcfilter, start)) > -1)
            {
                yield [start, filter.length];
                start += filter.length;
            }
        })(), highlight || template.filter);
    },

    highlightRegexp: function highlightRegexp(str, re, highlight)
    {
        return this.highlightSubstrings(str, (function ()
        {
            let res;
            while ((res = re.exec(str)) && res[0].length)
                yield [res.index, res[0].length];
        })(), highlight || template.filter);
    },

    highlightSubstrings: function highlightSubstrings(str, iter, highlight)
    {
        if (typeof str == "xml")
            return str;
        if (str == "")
            return <>{str}</>;

        str = String(str).replace(" ", "\u00a0");
        let s = <></>;
        let start = 0;
        let n = 0;
        for (let [i, length] in iter)
        {
            if (n++ > 50) // Prevent infinite loops.
                return s + <>{str.substr(start)}</>;
            XML.ignoreWhitespace = false;
            s += <>{str.substring(start, i)}</>;
            s += highlight(str.substr(i, length));
            start = i + length;
        }
        return s + <>{str.substr(start)}</>;
    },

    // FIXME: why is href="#"?
    highlightURL: function highlightURL(str, force)
    {
        if (force || /^[a-zA-Z]+:\/\//.test(str))
            return {str};
        else
            return str;
    },

    commandOutput: function generic(xml)
    {
        return <>:{commandline.command}
{xml}</>;
    },

    // every item must have a .xml property which defines how to draw itself
    // @param headers is an array of strings, the text for the header columns
    genericTable: function genericTable(items, format)
    {
        completion.listCompleter(function (context) {
            context.filterFunc = null;
            if (format)
                context.format = format;
            context.completions = items;
        });
    },

    jumps: function jumps(index, elems)
    {
        // <e4x>
        return this.commandOutput(

                    jump	title	URI

                {
                    this.map(Iterator(elems), function ([idx, val])

                        {idx == index ? ">" : ""}
                        {Math.abs(idx - index)}
                        {val.title}
                        {val.URI.spec}

)
                }

);
        // </e4x>
    },

    options: function options(title, opts)
    {
        // <e4x>
        return this.commandOutput(

                    --- {title} ---

                {
                    this.map(opts, function (opt)

                            {opt.pre}{opt.name}{opt.value}
                            {opt.isDefault || opt.default == null ? "" :  (default: {opt.default})}

)
                }

);
        // </e4x>
    },

    table: function table(title, data, indent)
    {
        let table =
        // <e4x>

                    {title}

                {
                    this.map(data, function (datum)

                       {datum[0]}
                       {template.maybeXML(datum[1])}

)
                }

;
        // </e4x>
        if (table.tr.length() > 1)
            return table;
    },

    tabular: function tabular(headings, style, iter)
    {
        // TODO: This might be mind-bogglingly slow. We'll see.
        // <e4x>
        return this.commandOutput(

                {
                    this.map(headings, function (h)
                    {h}	)
                }

                {
                    this.map(iter, function (row)

                    {
                        template.map(Iterator(row), function ([i, d])
                        {d}	)
                    }

)
                }

);
        // </e4x>
    },

    usage: function usage(iter)
    {
        // <e4x>
        return this.commandOutput(

            {
                this.map(iter, function (item)

                    {item.name || item.names[0]}
                    {item.description}

)
            }

);
        // </e4x>
    }
};

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>Fix template.highlightURL<commit_after>/***** BEGIN LICENSE BLOCK ***** {{{
 Copyright © 2008-2009 by Kris Maglione <maglione.k at Gmail>
 Distributable under the terms of the MIT license, which allows
 for sublicensing under any compatible license, including the MPL,
 GPL, and MPL. Anyone who changes this file is welcome to relicense
 it under any or all of those licenseses.
}}} ***** END LICENSE BLOCK *****/

/** @scope modules */

const template = {
    add: function add(a, b) a + b,
    join: function join(c) function (a, b) a + c + b,

    map: function map(iter, fn, sep, interruptable)
    {
        if (iter.length) // FIXME: Kludge?
            iter = util.Array.iterator(iter);
        let ret = <></>;
        let n = 0;
        for each (let i in Iterator(iter))
        {
            let val = fn(i);
            if (val == undefined)
                continue;
            if (sep && n++)
                ret += sep;
            if (interruptable && n % interruptable == 0)
                liberator.threadYield(true, true);
            ret += val;
        }
        return ret;
    },

    maybeXML: function maybeXML(xml)
    {
        if (typeof xml == "xml")
            return xml;
        try
        {
            return new XMLList(xml);
        }
        catch (e) {}
        return <>{xml}</>;
    },

    completionRow: function completionRow(item, class)
    {
        if (typeof icon == "function")
            icon = icon();

        if (class)
        {
            var text = item[0] || "";
            var desc = item[1] || "";
        }
        else
        {
            var text = this.process[0].call(this, item, item.text);
            var desc = this.process[1].call(this, item, item.description);
        }

        // <e4x>
        return

{text}

{desc}

;
        // </e4x>
    },

    bookmarkDescription: function (item, text)
    <>
        {text}
        {
            !(item.extra && item.extra.length) ? "" :

                ({
                    template.map(item.extra, function (e)
                    <>{e[0]}: {e[1]}</>,
                    <> </>/* Non-breaking space */)
                })

        }
    </>,

    icon: function (item, text)
    {
        return <>{item.icon ?  : <></>}{text}</>
    },

    filter: function (str) {str},

    gradient: function (left, right)

                    { template.map(util.range(0, 100), function (i)
                      ) }

,

    // if "processStrings" is true, any passed strings will be surrounded by " and
    // any line breaks are displayed as \n
    highlight: function highlight(arg, processStrings, clip)
    {
        // some objects like window.JSON or getBrowsers()._browsers need the try/catch
        try
        {
            let str = clip ? util.clip(String(arg), clip) : String(arg);
            switch (arg == null ? "undefined" : typeof arg)
            {
                case "number":
                    return {str};
                case "string":
                    if (processStrings)
                        str = str.quote();
                    return {str};
                case "boolean":
                    return {str};
                case "function":
                    // Vim generally doesn't like /foo*/, because */ looks like a comment terminator.
                    // Using /foo*(:?)/ instead.
                    if (processStrings)
                        return {str.replace(/\{(.|\n)*(?:)/g, "{ ... }")};
                    return <>{arg}</>;
                case "undefined":
                    return {arg};
                case "object":
                    // for java packages value.toString() would crash so badly
                    // that we cannot even try/catch it
                    if (/^\[JavaPackage.*\]$/.test(arg))
                        return <>[JavaPackage]</>;
                    if (processStrings && false)
                        str = template.highlightFilter(str, "\n", function () ^J);
                    return {str};
                case "xml":
                    return arg;
                default:
                    return <![CDATA[<unknown type>]]>;
            }
        }
        catch (e)
        {
            return<![CDATA[<unknown>]]>;
        }
    },

    highlightFilter: function highlightFilter(str, filter, highlight)
    {
        return this.highlightSubstrings(str, (function ()
        {
            if (filter.length == 0)
                return;
            let lcstr = String.toLowerCase(str);
            let lcfilter = filter.toLowerCase();
            let start = 0;
            while ((start = lcstr.indexOf(lcfilter, start)) > -1)
            {
                yield [start, filter.length];
                start += filter.length;
            }
        })(), highlight || template.filter);
    },

    highlightRegexp: function highlightRegexp(str, re, highlight)
    {
        return this.highlightSubstrings(str, (function ()
        {
            let res;
            while ((res = re.exec(str)) && res[0].length)
                yield [res.index, res[0].length];
        })(), highlight || template.filter);
    },

    highlightSubstrings: function highlightSubstrings(str, iter, highlight)
    {
        if (typeof str == "xml")
            return str;
        if (str == "")
            return <>{str}</>;

        str = String(str).replace(" ", "\u00a0");
        let s = <></>;
        let start = 0;
        let n = 0;
        for (let [i, length] in iter)
        {
            if (n++ > 50) // Prevent infinite loops.
                return s + <>{str.substr(start)}</>;
            XML.ignoreWhitespace = false;
            s += <>{str.substring(start, i)}</>;
            s += highlight(str.substr(i, length));
            start = i + length;
        }
        return s + <>{str.substr(start)}</>;
    },

    // FIXME: why is href="#"?
    highlightURL: function highlightURL(str, force)
    {
        if (force || /^[a-zA-Z]+:\/\//.test(str))
            return {str};
        else
            return str;
    },

    commandOutput: function generic(xml)
    {
        return <>:{commandline.command}
{xml}</>;
    },

    // every item must have a .xml property which defines how to draw itself
    // @param headers is an array of strings, the text for the header columns
    genericTable: function genericTable(items, format)
    {
        completion.listCompleter(function (context) {
            context.filterFunc = null;
            if (format)
                context.format = format;
            context.completions = items;
        });
    },

    jumps: function jumps(index, elems)
    {
        // <e4x>
        return this.commandOutput(

                    jump	title	URI

                {
                    this.map(Iterator(elems), function ([idx, val])

                        {idx == index ? ">" : ""}
                        {Math.abs(idx - index)}
                        {val.title}
                        {val.URI.spec}

)
                }

);
        // </e4x>
    },

    options: function options(title, opts)
    {
        // <e4x>
        return this.commandOutput(

                    --- {title} ---

                {
                    this.map(opts, function (opt)

                            {opt.pre}{opt.name}{opt.value}
                            {opt.isDefault || opt.default == null ? "" :  (default: {opt.default})}

)
                }

);
        // </e4x>
    },

    table: function table(title, data, indent)
    {
        let table =
        // <e4x>

                    {title}

                {
                    this.map(data, function (datum)

                       {datum[0]}
                       {template.maybeXML(datum[1])}

)
                }

;
        // </e4x>
        if (table.tr.length() > 1)
            return table;
    },

    tabular: function tabular(headings, style, iter)
    {
        // TODO: This might be mind-bogglingly slow. We'll see.
        // <e4x>
        return this.commandOutput(

                {
                    this.map(headings, function (h)
                    {h}	)
                }

                {
                    this.map(iter, function (row)

                    {
                        template.map(Iterator(row), function ([i, d])
                        {d}	)
                    }

)
                }

);
        // </e4x>
    },

    usage: function usage(iter)
    {
        // <e4x>
        return this.commandOutput(

            {
                this.map(iter, function (item)

                    {item.name || item.names[0]}
                    {item.description}

)
            }

);
        // </e4x>
    }
};

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['can/util/library', 'can/model'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('can/util/library'), require('can/model'));
    }
}(this, function (can, Model) {
    var stripSlashes = function (name) {
      return name.replace(/^\/|\/$/g, '');
    };

    var socketTypes = {
      primus: 'primus/primus.js',
      socketio: 'socket.io/socket.io.js'
    };

    var SocketModel = Model.extend({
      // Here we store locks that prevent disaptching
      // created, updated and removed events multiple times
      _locks: {},
      connect: can.Deferred(),
      setup: function () {
        Model.setup.apply(this, arguments);

        if(this.resource) {
          var self = this;
          var resource = stripSlashes(this.resource);
          // Creates a resource event handler function for a given event
          var makeHandler = function(ev) {
            return function(data) {
              var id = data[self.id];
              // Check if this id is locked and contains e.g. a 'created'
              // Which means that we are getting the duplicate event from the
              // Socket remove the lock but don't dispatch the event
              if(self._locks[id] === ev + 'd') {
                delete self._locks[id];
                // If we are currently updating or removing we ignore all
                // other resource events for this model instance
              } else if(self._locks[id] !== ev) {
                // Mapping from CanJS to Feathers event name
                var modelEvent = ev === 'remove' ? 'destroyed' : ev + 'd';

                // Don't trigger the create event on the model if it's
                // already in the model store, but still trigger other events.
                if (!(ev =='create' && self.store[id])) {
                  // Trigger the event from the resource as a model event
                  var model = self.model(data);
                  if(model[modelEvent]) {
                    model[modelEvent]();
                  }
                }
              }
            };
          };

          this.connect.done(function(socket) {
            socket.on(resource + ' created', makeHandler('create'));
            socket.on(resource + ' updated', makeHandler('update'));
            socket.on(resource + ' removed', makeHandler('remove'));
          });
        }
      },

      send: function () {
        var deferred = can.Deferred();
        var args = can.makeArray(arguments);
        var name = args[0];
        var self = this;

        // Turn something like 'find' into todos::find
        args[0] = stripSlashes(this.resource) + '::' + name;

        // Add the success callback which just resolves or fails the Deferred
        args.push(function (error, data) {
          if (error) {
            return deferred.reject(error);
          }

          // Set the lock so that we don't dispatch duplicate events
          if(name === 'create' || name === 'remove' || name === 'update') {
            self._locks[data[self.id]] = name + 'd';
          }

          deferred.resolve(data);
        });

        this.connect.done(function(socket) {
          socket.emit.apply(socket, args);
        });

        return deferred;
      },

      findAll: function (params) {
        if (params) {
          params = {query:params};
        }
        return this.send('find', params || {});
      },

      findOne: function (params) {
        return this.send('get', params.id, params);
      },

      create: function (attrs, params) {
        return this.send('create', attrs, params || {});
      },

      update: function (id, attrs, params) {
        var ev = this._locks[id] = 'update';
        return this.send(ev, id, attrs, params || {});
      },

      destroy: function (id, params) {
        var ev = this._locks[id] = 'remove';
        return this.send(ev, id, params || {});
      }
    }, {});

    can.Feathers = {
      Model: SocketModel,

      model: function (resource) {
        return SocketModel.extend({
          resource: resource
        }, {});
      },

      connect: function(options, queryObj) {
        options = options || {};

        // Load the Socket library (SocketIO or Primus) into a Script tag
        var dfd = SocketModel.connect;

        // We should only connect once
        if(dfd.state() !== 'resolved') {
          var script = document.createElement('script');
          // Resolves the Deferred when the script is loaded
          var resolve = function () {
            // Connect to the socket via the library given
            var provider = options.type === 'primus' ? Primus : io;
            // Use the query object, if provided.
            if (queryObj) {
              dfd.resolve(provider.connect(options.host, queryObj));
            } else {
              dfd.resolve(provider.connect(options.host));
            }
          };

          script.setAttribute('type', 'text/javascript');
          script.setAttribute('src', (options.host || '') +
            '/' + socketTypes[options.type || 'socketio']);

          script.onload = resolve;

          script.onreadystatechange = function () {
            if (this.readyState === 'complete') {
              resolve();
            }
          };

          document.body.appendChild(script);
        }

        return dfd;
      }
    };

    return can.Feathers;
}));
<commit_msg>Adding proper attachment to window which was broken in #4<commit_after>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['can/util/library', 'can/model'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('can/util/library'), require('can/model'));
    } else if(typeof window !== 'undefined' && root && root.can) {
        factory(root.can, root.can.Model);
    }
}(this, function (can, Model) {
    var stripSlashes = function (name) {
      return name.replace(/^\/|\/$/g, '');
    };

    var socketTypes = {
      primus: 'primus/primus.js',
      socketio: 'socket.io/socket.io.js'
    };

    var SocketModel = Model.extend({
      // Here we store locks that prevent disaptching
      // created, updated and removed events multiple times
      _locks: {},
      connect: can.Deferred(),
      setup: function () {
        Model.setup.apply(this, arguments);

        if(this.resource) {
          var self = this;
          var resource = stripSlashes(this.resource);
          // Creates a resource event handler function for a given event
          var makeHandler = function(ev) {
            return function(data) {
              var id = data[self.id];
              // Check if this id is locked and contains e.g. a 'created'
              // Which means that we are getting the duplicate event from the
              // Socket remove the lock but don't dispatch the event
              if(self._locks[id] === ev + 'd') {
                delete self._locks[id];
                // If we are currently updating or removing we ignore all
                // other resource events for this model instance
              } else if(self._locks[id] !== ev) {
                // Mapping from CanJS to Feathers event name
                var modelEvent = ev === 'remove' ? 'destroyed' : ev + 'd';

                // Don't trigger the create event on the model if it's
                // already in the model store, but still trigger other events.
                if (!(ev =='create' && self.store[id])) {
                  // Trigger the event from the resource as a model event
                  var model = self.model(data);
                  if(model[modelEvent]) {
                    model[modelEvent]();
                  }
                }
              }
            };
          };

          this.connect.done(function(socket) {
            socket.on(resource + ' created', makeHandler('create'));
            socket.on(resource + ' updated', makeHandler('update'));
            socket.on(resource + ' removed', makeHandler('remove'));
          });
        }
      },

      send: function () {
        var deferred = can.Deferred();
        var args = can.makeArray(arguments);
        var name = args[0];
        var self = this;

        // Turn something like 'find' into todos::find
        args[0] = stripSlashes(this.resource) + '::' + name;

        // Add the success callback which just resolves or fails the Deferred
        args.push(function (error, data) {
          if (error) {
            return deferred.reject(error);
          }

          // Set the lock so that we don't dispatch duplicate events
          if(name === 'create' || name === 'remove' || name === 'update') {
            self._locks[data[self.id]] = name + 'd';
          }

          deferred.resolve(data);
        });

        this.connect.done(function(socket) {
          socket.emit.apply(socket, args);
        });

        return deferred;
      },

      findAll: function (params) {
        if (params) {
          params = {query:params};
        }
        return this.send('find', params || {});
      },

      findOne: function (params) {
        return this.send('get', params.id, params);
      },

      create: function (attrs, params) {
        return this.send('create', attrs, params || {});
      },

      update: function (id, attrs, params) {
        var ev = this._locks[id] = 'update';
        return this.send(ev, id, attrs, params || {});
      },

      destroy: function (id, params) {
        var ev = this._locks[id] = 'remove';
        return this.send(ev, id, params || {});
      }
    }, {});

    can.Feathers = {
      Model: SocketModel,

      model: function (resource) {
        return SocketModel.extend({
          resource: resource
        }, {});
      },

      connect: function(options, queryObj) {
        options = options || {};

        // Load the Socket library (SocketIO or Primus) into a Script tag
        var dfd = SocketModel.connect;

        // We should only connect once
        if(dfd.state() !== 'resolved') {
          var script = document.createElement('script');
          // Resolves the Deferred when the script is loaded
          var resolve = function () {
            // Connect to the socket via the library given
            var provider = options.type === 'primus' ? Primus : io;
            // Use the query object, if provided.
            if (queryObj) {
              dfd.resolve(provider.connect(options.host, queryObj));
            } else {
              dfd.resolve(provider.connect(options.host));
            }
          };

          script.setAttribute('type', 'text/javascript');
          script.setAttribute('src', (options.host || '') +
            '/' + socketTypes[options.type || 'socketio']);

          script.onload = resolve;

          script.onreadystatechange = function () {
            if (this.readyState === 'complete') {
              resolve();
            }
          };

          document.body.appendChild(script);
        }

        return dfd;
      }
    };

    return can.Feathers;
}));
<|endoftext|>


<commit_before>var _ = require('underscore'),
    EventEmitter = require('events').EventEmitter,
    fs = require('fs'),
    path = require('path'),
    handlebars = require('handlebars');

const EMFILE_RETRY = 250;

var fileCache = {};

exports = module.exports = new EventEmitter();

function cacheRead(path, callback, exec) {
  path = exports.resolvePath(path);

  var cache = fileCache[path];
  if (cache) {
    if (cache.data) {
      callback(undefined, cache.data);
    } else {
      cache.pending.push(callback);
    }
    return;
  }

  cache = fileCache[path] = {
    pending: [callback]
  };

  exec(path, function _callback(err, data) {
    if (err && err.code === 'EMFILE') {
      setTimeout(exec.bind(this, path, _callback), EMFILE_RETRY);
    } else {
      if (err) {
        delete fileCache[path];
      }

      cache.data = data;
      cache.pending.forEach(function(callback) {
        callback(err, data);
      });
      exports.emit('cache:set', path);
    }
  });
}

exports.resetCache = function(filePath) {
  filePath = filePath && path.normalize(filePath);
  exports.emit('cache:reset', filePath);

  if (filePath) {
    filePath = exports.resolvePath(filePath);
    delete fileCache[filePath];
  } else {
    fileCache = {};
  }
};

var lookupPath;
exports.resolvePath = function(pathName) {
  // Poormans path.resolve. We aren't able to use the bundled path.resolve due to
  // it throwing sync EMFILE errors without a type to key on.
  if (lookupPath
      && (pathName[0] !== '/' && pathName.indexOf(':/') === -1 && pathName.indexOf(':\\') === -1)
      && pathName.indexOf(lookupPath) !== 0) {
    return lookupPath + pathName;
  } else {
    return pathName;
  }
};
exports.makeRelative = function(pathName) {
  if (pathName.indexOf(lookupPath) === 0) {
    return pathName.substring(lookupPath.length);
  } else {
    return pathName;
  }
};

exports.lookupPath = function(pathName) {
  if (pathName !== undefined) {
    lookupPath = pathName;
    if (lookupPath && !/\/$/.test(lookupPath)) {
      lookupPath += '/';
    }
  }
  return lookupPath;
};

exports.stat = function(file, callback) {
  fs.stat(file, function(err, stat) {
    if (err && err.code === 'EMFILE') {
      setTimeout(exports.stat.bind(exports, file, callback), EMFILE_RETRY);
    } else {
      callback(err, stat);
    }
  });
};

exports.readFileSync = function(file) {
  return fs.readFileSync(exports.resolvePath(file));
};
exports.readFile = function(file, callback) {
  cacheRead(file, callback, function(file, callback) {
    fs.readFile(file, callback);
  });
};
exports.readdir = function(dir, callback) {
  cacheRead(dir, callback, fs.readdir.bind(fs));
};

exports.ensureDirs = function(pathname, callback) {
  var dirname = path.dirname(pathname)
  exports.stat(dirname, function(err, stat) {
    if (err && err.code === 'ENOENT') {
      // If we don't exist, check to see if our parent exists before trying to create ourselves
      exports.ensureDirs(dirname, function(err) {
        fs.mkdir(dirname, 0755, function _callback(err) {
          if (err && err.code === 'EMFILE') {
            setTimeout(fs.mkdir.bind(fs, dirnane, 0755, _callback), EMFILE_RETRY);
          } else {
            // Off to the races... and we lost.
            callback(err && err.code === 'EEXIST' ? undefined : err);
          }
        });
      });
    } else {
      callback();
    }
  });
};

exports.writeFile = function(file, data, callback) {
  exports.resetCache(file);

  exports.ensureDirs(file, function(err) {
    if (err) {
      return callback(err);
    }

    fs.writeFile(file, data, 'utf8', function _callback(err) {
      if (err && err.code === 'EMFILE') {
        setTimeout(fs.writeFile.bind(fs, file, data, 'utf8', _callback), EMFILE_RETRY);
      } else {
        callback(err);
      }
    });
  });
};

exports.fileList = function(pathname, extension, callback, dirList, resource, srcDir) {
  if (_.isFunction(extension)) {
    callback = extension;
    extension = /.*/;
  }

  if (_.isArray(pathname)) {
    var files = pathname;
    pathname = '';
    if (!files.length) {
      return callback(undefined, []);
    }
    return handleFiles(false, undefined, _.uniq(files));
  } else if (!dirList) {
    if (pathname.src) {
      resource = resource || pathname;
      pathname = pathname.src;
    }

    pathname = exports.resolvePath(pathname);
  }
  if (resource && resource.src) {
    resource = _.clone(resource);
    delete resource.src;
  }

  function handleFiles(dirname, err, files, srcDir) {
    if (err) {
      return callback(err);
    }

    var ret = [],
        count = 0,
        expected = files.length,
        prefix = pathname ? pathname.replace(/\/$/, '') + '/' : '';

    function complete(files, index) {
      count++;

      ret[index] = files;

      if (count === expected) {
        ret = _.flatten(ret);

        if (srcDir) {
          ret = ret.map(function(file) {
            if (!file.src && !file.dir) {
              file = { src: file };
            }
            file.srcDir = srcDir;
            return file;
          });
        }

        if (dirname) {
          ret.push(_.defaults({dir: dirname}, resource));
          ret = ret.sort(function(a, b) {
            return (a.dir || a.src || a).localeCompare(b.dir || b.src || b);
          });
        }

        callback(undefined, ret);
      }
    }

    if (!files.length) {
      callback(undefined, []);
    }

    files.forEach(function(file, index) {
      var fileResource = resource;
      if (file.src) {
        fileResource = resource || file;
        file = file.src;
      } else if (_.isObject(file)) {
        complete(file, index);
        return;
      }

      exports.fileList(prefix + file, extension, function(err, files) {
        if (err) {
          callback(err);
          return;
        }

        complete(files, index);
      }, dirname, fileResource, srcDir);
    });
  }

  exports.stat(pathname, function(err, stat) {
    if (err) {
      if (err.code === 'ENOENT') {
        callback(undefined, [ _.extend({src: exports.makeRelative(pathname), enoent: true}, resource) ]);
      } else {
        callback(err);
      }
      return;
    }

    if (stat.isDirectory()) {
      var self = this;
      exports.readdir(pathname, function(err, files) {
        var _pathname = exports.makeRelative(pathname);
        handleFiles(_pathname, undefined, files, srcDir || _pathname);
      });
    } else {
      pathname = exports.makeRelative(pathname);

      var basename = path.basename(pathname),
          namePasses = basename[0] !== '.' && basename !== 'vendor' && (!dirList || extension.test(pathname)),
          ret = [];
      if (namePasses) {
        if (resource) {
          ret = [ _.defaults({src: pathname, srcDir: srcDir}, resource) ];
        } else if (srcDir) {
          ret = [ { src: pathname, srcDir: srcDir } ];
        } else {
          ret = [ pathname ];
        }
      }
      callback(undefined, ret);
    }
  });
};

//accepts a template string or a filename ending in .handlebars
exports.loadTemplate = function(template, splitOnDelimiter, callback) {
  function compile(templateStr) {
    try {
      if (splitOnDelimiter) {
        callback(null, templateStr.split(splitOnDelimiter).map(function(bit) {
          return handlebars.compile(bit);
        }));
      } else {
        callback(null, handlebars.compile(templateStr));
      }
    } catch (e) {
      callback(e);
    }
  }
  if (template.match(/\.handlebars$/)) {
    exports.readFile(path.join(exports.lookupPath(), template), function(err, data) {
      if (err) {
        callback(err);
      } else {
        compile(data.toString());
      }
    });
  } else {
    compile(template);
  }
}<commit_msg>Slightly better docs<commit_after>var _ = require('underscore'),
    EventEmitter = require('events').EventEmitter,
    fs = require('fs'),
    path = require('path'),
    handlebars = require('handlebars');

const EMFILE_RETRY = 250;

var fileCache = {};

exports = module.exports = new EventEmitter();

function cacheRead(path, callback, exec) {
  path = exports.resolvePath(path);

  var cache = fileCache[path];
  if (cache) {
    if (cache.data) {
      callback(undefined, cache.data);
    } else {
      cache.pending.push(callback);
    }
    return;
  }

  cache = fileCache[path] = {
    pending: [callback]
  };

  exec(path, function _callback(err, data) {
    if (err && err.code === 'EMFILE') {
      setTimeout(exec.bind(this, path, _callback), EMFILE_RETRY);
    } else {
      if (err) {
        delete fileCache[path];
      }

      cache.data = data;
      cache.pending.forEach(function(callback) {
        callback(err, data);
      });
      exports.emit('cache:set', path);
    }
  });
}

exports.resetCache = function(filePath) {
  filePath = filePath && path.normalize(filePath);
  exports.emit('cache:reset', filePath);

  if (filePath) {
    filePath = exports.resolvePath(filePath);
    delete fileCache[filePath];
  } else {
    fileCache = {};
  }
};

var lookupPath;
exports.resolvePath = function(pathName) {
  // Poormans path.resolve. We aren't able to use the bundled path.resolve due to
  // it throwing sync EMFILE errors without a type to key on.
  if (lookupPath
      && (pathName[0] !== '/' && pathName.indexOf(':/') === -1 && pathName.indexOf(':\\') === -1)
      && pathName.indexOf(lookupPath) !== 0) {
    return lookupPath + pathName;
  } else {
    return pathName;
  }
};
exports.makeRelative = function(pathName) {
  if (pathName.indexOf(lookupPath) === 0) {
    return pathName.substring(lookupPath.length);
  } else {
    return pathName;
  }
};

exports.lookupPath = function(pathName) {
  if (pathName !== undefined) {
    lookupPath = pathName;
    if (lookupPath && !/\/$/.test(lookupPath)) {
      lookupPath += '/';
    }
  }
  return lookupPath;
};

exports.stat = function(file, callback) {
  fs.stat(file, function(err, stat) {
    if (err && err.code === 'EMFILE') {
      setTimeout(exports.stat.bind(exports, file, callback), EMFILE_RETRY);
    } else {
      callback(err, stat);
    }
  });
};

exports.readFileSync = function(file) {
  return fs.readFileSync(exports.resolvePath(file));
};
exports.readFile = function(file, callback) {
  cacheRead(file, callback, function(file, callback) {
    fs.readFile(file, callback);
  });
};
exports.readdir = function(dir, callback) {
  cacheRead(dir, callback, fs.readdir.bind(fs));
};

exports.ensureDirs = function(pathname, callback) {
  var dirname = path.dirname(pathname)
  exports.stat(dirname, function(err, stat) {
    if (err && err.code === 'ENOENT') {
      // If we don't exist, check to see if our parent exists before trying to create ourselves
      exports.ensureDirs(dirname, function(err) {
        fs.mkdir(dirname, 0755, function _callback(err) {
          if (err && err.code === 'EMFILE') {
            setTimeout(fs.mkdir.bind(fs, dirnane, 0755, _callback), EMFILE_RETRY);
          } else {
            // Off to the races... and we lost.
            callback(err && err.code === 'EEXIST' ? undefined : err);
          }
        });
      });
    } else {
      callback();
    }
  });
};

exports.writeFile = function(file, data, callback) {
  exports.resetCache(file);

  exports.ensureDirs(file, function(err) {
    if (err) {
      return callback(err);
    }

    fs.writeFile(file, data, 'utf8', function _callback(err) {
      if (err && err.code === 'EMFILE') {
        setTimeout(fs.writeFile.bind(fs, file, data, 'utf8', _callback), EMFILE_RETRY);
      } else {
        callback(err);
      }
    });
  });
};

/**
 * Takes a given input and returns the files that are represented.
 *
 * pathname may be:
 *   a resource object
 *   a path on the file system
 *   an array of resources
 */
exports.fileList = function(pathname, extension, callback, dirList, resource, srcDir) {
  if (_.isFunction(extension)) {
    callback = extension;
    extension = /.*/;
  }

  if (_.isArray(pathname)) {
    var files = pathname;
    pathname = '';
    if (!files.length) {
      return callback(undefined, []);
    }
    return handleFiles(false, undefined, _.uniq(files));
  } else if (!dirList) {
    if (pathname.src) {
      resource = resource || pathname;
      pathname = pathname.src;
    }

    pathname = exports.resolvePath(pathname);
  }
  if (resource && resource.src) {
    resource = _.clone(resource);
    delete resource.src;
  }

  function handleFiles(dirname, err, files, srcDir) {
    if (err) {
      return callback(err);
    }

    var ret = [],
        count = 0,
        expected = files.length,
        prefix = pathname ? pathname.replace(/\/$/, '') + '/' : '';

    function complete(files, index) {
      count++;

      ret[index] = files;

      if (count === expected) {
        ret = _.flatten(ret);

        if (srcDir) {
          ret = ret.map(function(file) {
            if (!file.src && !file.dir) {
              file = { src: file };
            }
            file.srcDir = srcDir;
            return file;
          });
        }

        if (dirname) {
          ret.push(_.defaults({dir: dirname}, resource));
          ret = ret.sort(function(a, b) {
            return (a.dir || a.src || a).localeCompare(b.dir || b.src || b);
          });
        }

        callback(undefined, ret);
      }
    }

    if (!files.length) {
      callback(undefined, []);
    }

    files.forEach(function(file, index) {
      var fileResource = resource;
      if (file.src) {
        fileResource = resource || file;
        file = file.src;
      } else if (_.isObject(file)) {
        complete(file, index);
        return;
      }

      exports.fileList(prefix + file, extension, function(err, files) {
        if (err) {
          callback(err);
          return;
        }

        complete(files, index);
      }, dirname, fileResource, srcDir);
    });
  }

  exports.stat(pathname, function(err, stat) {
    if (err) {
      if (err.code === 'ENOENT') {
        callback(undefined, [ _.extend({src: exports.makeRelative(pathname), enoent: true}, resource) ]);
      } else {
        callback(err);
      }
      return;
    }

    if (stat.isDirectory()) {
      var self = this;
      exports.readdir(pathname, function(err, files) {
        var _pathname = exports.makeRelative(pathname);
        handleFiles(_pathname, undefined, files, srcDir || _pathname);
      });
    } else {
      pathname = exports.makeRelative(pathname);

      var basename = path.basename(pathname),
          namePasses = basename[0] !== '.' && basename !== 'vendor' && (!dirList || extension.test(pathname)),
          ret = [];
      if (namePasses) {
        if (resource) {
          ret = [ _.defaults({src: pathname, srcDir: srcDir}, resource) ];
        } else if (srcDir) {
          ret = [ { src: pathname, srcDir: srcDir } ];
        } else {
          ret = [ pathname ];
        }
      }
      callback(undefined, ret);
    }
  });
};

//accepts a template string or a filename ending in .handlebars
exports.loadTemplate = function(template, splitOnDelimiter, callback) {
  function compile(templateStr) {
    try {
      if (splitOnDelimiter) {
        callback(null, templateStr.split(splitOnDelimiter).map(function(bit) {
          return handlebars.compile(bit);
        }));
      } else {
        callback(null, handlebars.compile(templateStr));
      }
    } catch (e) {
      callback(e);
    }
  }
  if (template.match(/\.handlebars$/)) {
    exports.readFile(path.join(exports.lookupPath(), template), function(err, data) {
      if (err) {
        callback(err);
      } else {
        compile(data.toString());
      }
    });
  } else {
    compile(template);
  }
}
<|endoftext|>