<commit_before>'use strict';

const chai = require('chai');
const expect = chai.expect;
chai.use(require('chai-things'));
const mockery = require('mockery');
const upath = require('upath');
const sinon = require('sinon');

let Ravel, conf, coreSymbols;

describe('Ravel', () => {
  beforeEach((done) => {
    // enable mockery
    mockery.enable({
      useCleanCache: true,
      warnOnReplace: false,
      warnOnUnregistered: false
    });
    mockery.registerMock('redis', require('redis-mock'));
    Ravel = new (require('../../lib/ravel'))();
    coreSymbols = require('../../lib/core/symbols');
    Ravel.log.setLevel('NONE');
    done();
  });

  afterEach((done) => {
    Ravel = undefined;
    coreSymbols = undefined;
    mockery.deregisterAll();
    mockery.disable();
    done();
  });

  describe('#set()', () => {
    it('should allow clients to set the value of a parameter', (done) => {
      Ravel.registerParameter('test param', false);
      Ravel.set('test param', 'test value');
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal('test value');
      done();
    });

    it('should throw a Ravel.ApplicationError.IllegalValue error when a client attempts to set an unknown parameter', (done) => {
      try {
        Ravel.set('unknown param', 'test value');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.IllegalValue);
        done();
      }
    });
  });

  describe('#get()', () => {
    it('should allow clients to retrieve the value of a set optional parameter', (done) => {
      Ravel.registerParameter('test param', false);
      Ravel.set('test param', 'test value');
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal('test value');
      done();
    });

    it('should return undefined when clients attempt to retrieve the value of an unset optional parameter', (done) => {
      Ravel.registerParameter('test param', false);
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal(undefined);
      done();
    });

    it('should allow clients to retrieve the value of a set required parameter', (done) => {
      Ravel.registerParameter('test param', true);
      Ravel.set('test param', 'test value');
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal('test value');
      done();
    });

    it('should throw a Ravel.ApplicationError.General error when clients attempt to retrieve a parameter before loading', (done) => {
      try {
        Ravel[coreSymbols.parametersLoaded] = false;
        Ravel.get('test param');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.General);
        done();
      }
    });

    it('should throw a Ravel.ApplicationError.NotFound error when clients attempt to retrieve an unregistered parameter', (done) => {
      try {
        Ravel[coreSymbols.parametersLoaded] = true;
        Ravel.get('test param');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.NotFound);
        done();
      }
    });

    it('should throw a Ravel.ApplicationError.NotFound error when clients attempt to retrieve the value of an unset required parameter', (done) => {
      try {
        Ravel.registerParameter('test param', true);
        Ravel[coreSymbols.parametersLoaded] = true;
        Ravel.get('test param');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.NotFound);
        done();
      }
    });
  });

  describe('.config', () => {
    it('should return the full configuration of the given `ravel instance`', (done) => {
      const defaultConfig = Ravel.config;
      Ravel.registerParameter('test param', true);
      Ravel.registerParameter('test param 2', true);
      Ravel.set('test param', false);
      Ravel.set('test param 2', 10);

      const expected = {
        'test param': false,
        'test param 2': 10
      };
      Object.assign(expected, defaultConfig);

      expect(Ravel.config).to.deep.equal(expected);
      done();
    });
  });

  describe('#_validateParameters()', () => {
    it('should throw a Ravel.ApplicationError.NotFound error when there are any unset required parameters', (done) => {
      try {
        Ravel.registerParameter('test param', true);
        Ravel[coreSymbols.parametersLoaded] = true;
        Ravel[coreSymbols.validateParameters]();
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.NotFound);
        done();
      }
    });

    it('should re-throw unrelated errors', (done) => {
      try {
        Ravel[coreSymbols.validateParameters]();
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.General);
        done();
      }
    });
  });

  describe('#_loadParameters()', () => {
    it('should allow users to specify Ravel config parameters via a .ravelrc.json config file', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      // can't use extension on mock because mockery only works with exact matches
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), conf);
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(conf['redis port']);
      done();
    });

    it('should should support searching for .ravelrc.json files in any parent directory of app.cwd', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      const parent = Ravel.cwd.split(upath.sep).slice(0, -1).join(upath.sep);
      // can't use extension on mock because mockery only works with exact matches
      mockery.registerMock(upath.join(parent, '.ravelrc'), conf);
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(conf['redis port']);
      done();
    });

    it('should should support searching for .ravelrc.json files in any parent directory of app.cwd, including root', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      let root = Ravel.cwd.split(upath.sep).slice(0, 1).join(upath.sep);
      root = root.length > 0 ? root : upath.sep;
      // can't use extension on mock because mockery only works with exact matches
      mockery.registerMock(upath.join(root, '.ravelrc'), conf);
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(conf['redis port']);
      done();
    });

    it('should allow users to specify Ravel config parameters via a .ravelrc config file and parse it to JSON', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), JSON.stringify(conf));
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(conf['redis port']);
      done();
    });

    it('should not override parameters set programmatically via Ravel.set', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), conf);

      Ravel.set('redis port', 6380);
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(6380);
      done();
    });

    it('should throw a Ravel.ApplicationError.IllegalValue if an unregistered paramter is specified in the config file', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      conf[Math.random().toString()] = false;
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), conf);

      Ravel.set('redis port', 6380);
      expect(() => {
        Ravel[coreSymbols.loadParameters]();
      }).to.throw(Ravel.ApplicationError.IllegalValue);
      done();
    });

    it('should load defaults if no configuration files are present', async () => {
      const oldParams = {
        'redis host': '0.0.0.0',
        'redis port': 6379,
        'redis max retries': 10,
        'redis keepalive interval': 1000,
        'port': 8080,
        'app route': '/',
        'login route': '/login',
        'keygrip keys': ['123abc'],
        'session key': 'koa.sid',
        'session max age': null,
        'log level': 'DEBUG'
      };
      Ravel.set('keygrip keys', ['123abc']);
      await Ravel.init();
      // now load params from non-existent ravelrc file
      expect(Ravel.config).to.deep.equal(oldParams);
    });

    it('should throw a SyntaxError if a .ravelrc file is found but is malformed', (done) => {
      const m = require('module');
      const origLoad = m._load;
      const stub = sinon.stub(m, '_load').callsFake((...args) => {
        if (args[0] === upath.join(Ravel.cwd, '.ravelrc')) throw new SyntaxError();
        return origLoad.apply(m, args);
      });
      expect(() => { Ravel[coreSymbols.loadParameters](); }).to.throw(SyntaxError);
      stub.restore();
      done();
    });
  });
});
<commit_msg>Making ravelrc search tests more verbose for windows debugging.<commit_after>'use strict';

const chai = require('chai');
const expect = chai.expect;
chai.use(require('chai-things'));
const mockery = require('mockery');
const upath = require('upath');
const sinon = require('sinon');

let Ravel, conf, coreSymbols;

describe('Ravel', () => {
  beforeEach((done) => {
    // enable mockery
    mockery.enable({
      useCleanCache: true,
      warnOnReplace: false,
      warnOnUnregistered: false
    });
    mockery.registerMock('redis', require('redis-mock'));
    Ravel = new (require('../../lib/ravel'))();
    coreSymbols = require('../../lib/core/symbols');
    Ravel.log.setLevel('NONE');
    done();
  });

  afterEach((done) => {
    Ravel = undefined;
    coreSymbols = undefined;
    mockery.deregisterAll();
    mockery.disable();
    done();
  });

  describe('#set()', () => {
    it('should allow clients to set the value of a parameter', (done) => {
      Ravel.registerParameter('test param', false);
      Ravel.set('test param', 'test value');
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal('test value');
      done();
    });

    it('should throw a Ravel.ApplicationError.IllegalValue error when a client attempts to set an unknown parameter', (done) => {
      try {
        Ravel.set('unknown param', 'test value');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.IllegalValue);
        done();
      }
    });
  });

  describe('#get()', () => {
    it('should allow clients to retrieve the value of a set optional parameter', (done) => {
      Ravel.registerParameter('test param', false);
      Ravel.set('test param', 'test value');
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal('test value');
      done();
    });

    it('should return undefined when clients attempt to retrieve the value of an unset optional parameter', (done) => {
      Ravel.registerParameter('test param', false);
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal(undefined);
      done();
    });

    it('should allow clients to retrieve the value of a set required parameter', (done) => {
      Ravel.registerParameter('test param', true);
      Ravel.set('test param', 'test value');
      Ravel[coreSymbols.parametersLoaded] = true;
      expect(Ravel.get('test param')).to.equal('test value');
      done();
    });

    it('should throw a Ravel.ApplicationError.General error when clients attempt to retrieve a parameter before loading', (done) => {
      try {
        Ravel[coreSymbols.parametersLoaded] = false;
        Ravel.get('test param');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.General);
        done();
      }
    });

    it('should throw a Ravel.ApplicationError.NotFound error when clients attempt to retrieve an unregistered parameter', (done) => {
      try {
        Ravel[coreSymbols.parametersLoaded] = true;
        Ravel.get('test param');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.NotFound);
        done();
      }
    });

    it('should throw a Ravel.ApplicationError.NotFound error when clients attempt to retrieve the value of an unset required parameter', (done) => {
      try {
        Ravel.registerParameter('test param', true);
        Ravel[coreSymbols.parametersLoaded] = true;
        Ravel.get('test param');
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.NotFound);
        done();
      }
    });
  });

  describe('.config', () => {
    it('should return the full configuration of the given `ravel instance`', (done) => {
      const defaultConfig = Ravel.config;
      Ravel.registerParameter('test param', true);
      Ravel.registerParameter('test param 2', true);
      Ravel.set('test param', false);
      Ravel.set('test param 2', 10);

      const expected = {
        'test param': false,
        'test param 2': 10
      };
      Object.assign(expected, defaultConfig);

      expect(Ravel.config).to.deep.equal(expected);
      done();
    });
  });

  describe('#_validateParameters()', () => {
    it('should throw a Ravel.ApplicationError.NotFound error when there are any unset required parameters', (done) => {
      try {
        Ravel.registerParameter('test param', true);
        Ravel[coreSymbols.parametersLoaded] = true;
        Ravel[coreSymbols.validateParameters]();
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.NotFound);
        done();
      }
    });

    it('should re-throw unrelated errors', (done) => {
      try {
        Ravel[coreSymbols.validateParameters]();
        done(new Error('Should never reach this line.'));
      } catch (err) {
        expect(err).to.be.instanceof(Ravel.ApplicationError.General);
        done();
      }
    });
  });

  describe('#_loadParameters()', () => {
    it('should allow users to specify Ravel config parameters via a .ravelrc.json config file', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      // can't use extension on mock because mockery only works with exact matches
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), conf);
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(conf['redis port']);
      done();
    });

    it('should should support searching for .ravelrc.json files in any parent directory of app.cwd', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      let parent = Ravel.cwd.split(upath.sep).slice(0, -1).join(upath.sep);
      parent = parent.length > 0 ? parent : upath.sep;
      // can't use extension on mock because mockery only works with exact matches
      const joined = upath.join(parent, '.ravelrc');
      mockery.registerMock(joined, conf);
      Ravel[coreSymbols.loadParameters]();
      const msg = `Failed to find .ravelrc in ${joined}`;
      expect(Ravel.get('koa view engine'), msg).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port'), msg).to.equal(conf['redis port']);
      done();
    });

    it('should should support searching for .ravelrc.json files in any parent directory of app.cwd, including root', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      let root = Ravel.cwd.split(upath.sep).slice(0, 1).join(upath.sep);
      root = root.length > 0 ? root : upath.sep;
      // can't use extension on mock because mockery only works with exact matches
      const joined = upath.join(root, '.ravelrc');
      mockery.registerMock(upath.join(root, '.ravelrc'), conf);
      Ravel[coreSymbols.loadParameters]();
      const msg = `Failed to find .ravelrc in ${joined}`;
      expect(Ravel.get('koa view engine'), msg).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port'), msg).to.equal(conf['redis port']);
      done();
    });

    it('should allow users to specify Ravel config parameters via a .ravelrc config file and parse it to JSON', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), JSON.stringify(conf));
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(conf['redis port']);
      done();
    });

    it('should not override parameters set programmatically via Ravel.set', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), conf);

      Ravel.set('redis port', 6380);
      Ravel[coreSymbols.loadParameters]();
      expect(Ravel.get('koa view engine')).to.equal(conf['koa view engine']);
      expect(Ravel.get('redis port')).to.equal(6380);
      done();
    });

    it('should throw a Ravel.ApplicationError.IllegalValue if an unregistered paramter is specified in the config file', (done) => {
      conf = {
        'koa view engine': 'ejs',
        'redis port': 6379
      };
      conf[Math.random().toString()] = false;
      mockery.registerMock(upath.join(Ravel.cwd, '.ravelrc'), conf);

      Ravel.set('redis port', 6380);
      expect(() => {
        Ravel[coreSymbols.loadParameters]();
      }).to.throw(Ravel.ApplicationError.IllegalValue);
      done();
    });

    it('should load defaults if no configuration files are present', async () => {
      const oldParams = {
        'redis host': '0.0.0.0',
        'redis port': 6379,
        'redis max retries': 10,
        'redis keepalive interval': 1000,
        'port': 8080,
        'app route': '/',
        'login route': '/login',
        'keygrip keys': ['123abc'],
        'session key': 'koa.sid',
        'session max age': null,
        'log level': 'DEBUG'
      };
      Ravel.set('keygrip keys', ['123abc']);
      await Ravel.init();
      // now load params from non-existent ravelrc file
      expect(Ravel.config).to.deep.equal(oldParams);
    });

    it('should throw a SyntaxError if a .ravelrc file is found but is malformed', (done) => {
      const m = require('module');
      const origLoad = m._load;
      const stub = sinon.stub(m, '_load').callsFake((...args) => {
        if (args[0] === upath.join(Ravel.cwd, '.ravelrc')) throw new SyntaxError();
        return origLoad.apply(m, args);
      });
      expect(() => { Ravel[coreSymbols.loadParameters](); }).to.throw(SyntaxError);
      stub.restore();
      done();
    });
  });
});
<|endoftext|>


<commit_before>/** @flow */
import {assert} from 'chai'
import {createRepository} from '../src/createRepository'
import Squel from 'squel'
import select from '../src/query/select'

const METADATA_QUERY = 'SHOW COLUMNS FROM ??'
const uMetadata = [[
  {
    Field: 'id'
  },
  {
    Field: 'some'
  }
]]

function createStubManager() {
  return {
    getConnection() {
      return {
        query(sql) {
          if (sql == METADATA_QUERY) {
            return Promise.resolve(uMetadata)
          }
        }
      }
    },
    startQuery() {
      return {
        ...Squel,
        select: options => select(this, options)
      }
    },
    query() {
      throw new Error('You must override query method')
    }
  }
}

describe('createRepository', () => {
  it('should find data by id', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id = ?)'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 1)
        assert.include(values || [], 1)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const record = await uRepo.find(1)
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should findOne data by criteria', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id = ?) LIMIT 1'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 1)
        assert.include(values || [], 1)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const record = await uRepo.findOneBy({id: 1})
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should find data by criteria', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id = ?)'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 1)
        assert.include(values || [], 1)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const result = await uRepo.findBy({id: 1})
    assert.lengthOf(result, 1)
    const [record] = result
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should findOne data by hard criteria', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id IN (?, ?)) AND (status != ?) AND (name LIKE ?) AND (foo NOT IN (?, ?))'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 6)
        assert.include(values || [], 1)
        assert.include(values || [], 2)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const result = await uRepo.findBy({
      id: {
        $in: [1, 2],
      },
      status: {
        $not: 1
      },
      name: {
        $like: 'test'
      },
      foo: {
        $notIn: [1,2]
      }
    })
    assert.lengthOf(result, 1)
    const [record] = result
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should create data', async () => {
    const QUERY = 'INSERT INTO u (test, ololo) VALUES (?, ?)'
    const changes = {
      test : 'some',
      ololo: 'ololo'
    }
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 2)
        return Promise.resolve([{
          insertId: 1
        }])
      }
    }

    const uRepo = createRepository('u', manager)
    const result = await uRepo._save(changes)
    assert.equal(result, 1)
  })
  it('should update data', async () => {
    const QUERY = 'UPDATE u SET id = ?, test = ?, ololo = ? WHERE (id = ?)'
    const changes = {
      id: 1,
      test : 'some',
      ololo: 'ololo'
    }
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 4)
        return Promise.resolve([{}])
      }
    }

    const uRepo = createRepository('u', manager)
    await uRepo._save(changes, 1)
  })
})
<commit_msg>Fix flow checking for createRepository method in tests<commit_after>/** @flow */
import {assert} from 'chai'
import {createRepository} from '../src/createRepository'
import Squel from 'squel'
import select from '../src/query/select'

const METADATA_QUERY = 'SHOW COLUMNS FROM ??'
const uMetadata = [[
  {
    Field: 'id'
  },
  {
    Field: 'some'
  }
]]

function createStubManager() {
  return {
    getConnection() {
      return {
        query(sql) {
          if (sql == METADATA_QUERY) {
            return Promise.resolve(uMetadata)
          }
        }
      }
    },
    startQuery() {
      return {
        ...Squel,
        select: options => select(this, options)
      }
    },
    query() {
      throw new Error('You must override query method')
    },
    clear() {

    }
  }
}

describe('createRepository', () => {
  it('should find data by id', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id = ?)'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 1)
        assert.include(values || [], 1)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const record = await uRepo.find(1)
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should findOne data by criteria', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id = ?) LIMIT 1'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 1)
        assert.include(values || [], 1)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const record = await uRepo.findOneBy({id: 1})
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should find data by criteria', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id = ?)'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 1)
        assert.include(values || [], 1)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const result = await uRepo.findBy({id: 1})
    assert.lengthOf(result, 1)
    const [record] = result
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should findOne data by hard criteria', async () => {
    const QUERY = 'SELECT * FROM u WHERE (id IN (?, ?)) AND (status != ?) AND (name LIKE ?) AND (foo NOT IN (?, ?))'
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 6)
        assert.include(values || [], 1)
        assert.include(values || [], 2)
        return Promise.resolve([[{
          id: 1,
          some: 'field'
        }]])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const result = await uRepo.findBy({
      id: {
        $in: [1, 2],
      },
      status: {
        $not: 1
      },
      name: {
        $like: 'test'
      },
      foo: {
        $notIn: [1,2]
      }
    })
    assert.lengthOf(result, 1)
    const [record] = result
    assert.isNotNull(record)
    if (record == null) return //Flow hack
    assert.propertyVal(record, 'id', 1)
    assert.propertyVal(record, 'some', 'field')
  })
  it('should create data', async () => {
    const QUERY = 'INSERT INTO u (test, ololo) VALUES (?, ?)'
    const changes = {
      test : 'some',
      ololo: 'ololo'
    }
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 2)
        return Promise.resolve([{
          insertId: 1
        }])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    const result = await uRepo._save(changes)
    assert.equal(result, 1)
  })
  it('should update data', async () => {
    const QUERY = 'UPDATE u SET id = ?, test = ?, ololo = ? WHERE (id = ?)'
    const changes = {
      id: 1,
      test : 'some',
      ololo: 'ololo'
    }
    const manager = {
      ...createStubManager(),
      query(query) {
        const {text, values} = query.toParam()
        assert.equal(text, QUERY)
        assert.lengthOf(values, 4)
        return Promise.resolve([{}])
      }
    }
    // $FlowIgnore fix for model
    const uRepo = createRepository('u', manager)
    await uRepo._save(changes, 1)
  })
})
<|endoftext|>


<commit_before>var expect      = require('chai').expect;
var _           = require("lodash");
var creditcards = require("../src/creditcards.js");

describe("English locale tests (en_US)", function() {
    it ('returns "Visa" for "visa" (network name)', function() {
    	expect(creditcards.getCreditcardName("en_US", "visa")).to.equal("Visa");
    });

    it ('returns "Expiry Date" for "expiration" (field name)', function() {
    	expect(creditcards.getFieldName("en_US", "expiration")).to.equal("Expiry Date");
    });
});

describe("Invalid locale tests (xx_XX)", function() {
    it ('returns empty string for "visa" (network name)', function() {
    	expect(creditcards.getCreditcardName("xx_XX", "visa")).to.equal("");
    });

    it ('returns empty string for "expiration" (field name)', function() {
    	expect(creditcards.getFieldName("xx_XX", "expiration")).to.equal("");
    });
});
<commit_msg>Fixed path (case sensitive!)<commit_after>var expect      = require('chai').expect;
var _           = require("lodash");
var creditcards = require("../src/Creditcards.js");

describe("English locale tests (en_US)", function() {
    it ('returns "Visa" for "visa" (network name)', function() {
    	expect(creditcards.getCreditcardName("en_US", "visa")).to.equal("Visa");
    });

    it ('returns "Expiry Date" for "expiration" (field name)', function() {
    	expect(creditcards.getFieldName("en_US", "expiration")).to.equal("Expiry Date");
    });
});

describe("Invalid locale tests (xx_XX)", function() {
    it ('returns empty string for "visa" (network name)', function() {
    	expect(creditcards.getCreditcardName("xx_XX", "visa")).to.equal("");
    });

    it ('returns empty string for "expiration" (field name)', function() {
    	expect(creditcards.getFieldName("xx_XX", "expiration")).to.equal("");
    });
});
<|endoftext|>


<commit_before>/* utility package for jsPsych */

(function($) {

    jsPsych.utils = (function() {

        // public methods
        var public_object = {};

        // take the hierarchically arranged jsPsych.data object and turn it into
        // a one-dimensional array where each entry is data from a  single trial
        // append_data is optional and can be items that should be added to all
        // trials, such as a subject identifier
        public_object.flatten_data_object = function(data_object, append_data) {

            append_data = (typeof append_data === undefined) ? {} : append_data;

            var trials = [];

            // loop through data_object
            for (var i = 0; i < data_object.length; i++) {
                for (var j = 0; j < data_object[i].length; j++) {
                    var data = $.extend({}, data_object[i][j], append_data);
                    trials.push(data);
                }
            }

            return trials;
        }

        // method for drawing palmer stimuli.
        // returns the string description of svg element containing the stimulus
        // requires raphaeljs library -> www.raphaeljs.com
        public_object.draw_palmer_stimulus = function(square_size, grid_spacing, circle_radius, configuration) {

            // create a div to hold the generated svg object
            var stim_div = $('body').append('

');

            var size = grid_spacing * (square_size + 1);

            // create the svg raphael object
            var paper = Raphael("stim_palmer", size, size);

            // create the circles at the vertices.
            var circles = [];
            var node_idx = 0;
            for (var i = 1; i <= square_size; i++) {
                for (var j = 1; j <= square_size; j++) {
                    var circle = paper.circle(grid_spacing * j, grid_spacing * i, circle_radius);
                    circle.attr("fill", "#000").attr("stroke-width", "0").attr("stroke", "#000").data("node", node_idx);
                    node_idx++;
                    circles.push(circle);
                }
            }

            // create all possible lines that connect circles
            var horizontal_lines = [];
            var vertical_lines = [];
            var backslash_lines = [];
            var forwardslash_lines = [];

            for (var i = 0; i < square_size; i++) {
                for (var j = 0; j < square_size; j++) {
                    var current_item = (i * square_size) + j;
                    // add horizontal connections
                    if (j < (square_size - 1)) {
                        horizontal_lines.push([current_item, current_item + 1]);
                    }
                    // add vertical connections
                    if (i < (square_size - 1)) {
                        vertical_lines.push([current_item, current_item + square_size]);
                    }
                    // add diagonal backslash connections
                    if (i < (square_size - 1) && j < (square_size - 1)) {
                        backslash_lines.push([current_item, current_item + square_size + 1]);
                    }
                    // add diagonal forwardslash connections
                    if (i < (square_size - 1) && j > 0) {
                        forwardslash_lines.push([current_item, current_item + square_size - 1]);
                    }
                }
            }

            var lines = horizontal_lines.concat(vertical_lines).concat(backslash_lines).concat(forwardslash_lines);

            // actually draw the lines
            var lineIsVisible = [];
            var lineElements = [];

            for (var i = 0; i < lines.length; i++) {
                var line = paper.path("M" + circles[lines[i][0]].attr("cx") + " " + circles[lines[i][0]].attr("cy") + "L" + circles[lines[i][1]].attr("cx") + " " + circles[lines[i][1]].attr("cy")).attr("stroke-width", "8").attr("stroke", "#000");
                line.hide();
                lineElements.push(line);
                lineIsVisible.push(0);
            }

            // define some helper functions to toggle lines on and off

            // this function turns a line on/off based on the index (the_line)
            function toggle_line(the_line) {
                if (the_line > -1) {
                    if (lineIsVisible[the_line] === 0) {
                        lineElements[the_line].show();
                        lineElements[the_line].toBack();
                        lineIsVisible[the_line] = 1;
                    }
                    else {
                        lineElements[the_line].hide();
                        lineElements[the_line].toBack();
                        lineIsVisible[the_line] = 0;
                    }
                }
            }

            // displays the line wherever there
            // is a 1 in the array.
            // showConfiguration(configuration)
            for (var i = 0; i < configuration.length; i++) {
                if (configuration[i] == 1) {
                    toggle_line(i);
                }
            }

            var svg = $("#stim_palmer").html();

            $('#stim_palmer').remove();

            return svg;
        };

        public_object.palmer_add_random_connected_element = function(square_size, configuration) {
            // make sure that configuration is not ALL 1's
            if ($.inArray(0, configuration) == -1) {
                return configuration;
            }

            // create all possible lines that connect circles
            var horizontal_lines = [];
            var vertical_lines = [];
            var backslash_lines = [];
            var forwardslash_lines = [];

            for (var i = 0; i < square_size; i++) {
                for (var j = 0; j < square_size; j++) {
                    var current_item = (i * square_size) + j;
                    // add horizontal connections
                    if (j < (square_size - 1)) {
                        horizontal_lines.push([current_item, current_item + 1]);
                    }
                    // add vertical connections
                    if (i < (square_size - 1)) {
                        vertical_lines.push([current_item, current_item + square_size]);
                    }
                    // add diagonal backslash connections
                    if (i < (square_size - 1) && j < (square_size - 1)) {
                        backslash_lines.push([current_item, current_item + square_size + 1]);
                    }
                    // add diagonal forwardslash connections
                    if (i < (square_size - 1) && j > 0) {
                        forwardslash_lines.push([current_item, current_item + square_size - 1]);
                    }
                }
            }

            var lines = horizontal_lines.concat(vertical_lines).concat(backslash_lines).concat(forwardslash_lines);

            function build_possible_lines_list() {
                // get a list of all the lines that exist in the passed configuration
                var possible_lines_to_connect_from = [];
                $.each(configuration, function(i, v) {
                    if (v == 1) {
                        possible_lines_to_connect_from.push(i);
                    }
                });

                // randomly select one of the lines
                var choice = possible_lines_to_connect_from[Math.floor(Math.random() * possible_lines_to_connect_from.length)];

                // randomly select which end of the line to connect to
                var circles = lines[choice];

                // build a list of all possible lines that contain the circles
                var possible_new_lines = [];
                for(var i=0;i<circles.length;i++){
                    $.each(lines, function(index, v) {
                        if ($.inArray(circles[i], v) > -1) {
                            if (configuration[index] === 0) { // this excludes lines that already exist
                                possible_new_lines.push(index);
                            }
                        }
                    });
                }

                return possible_new_lines;
            }

            var possible_new_lines = build_possible_lines_list();

            // make sure there are lines to add
            /*while (possible_new_lines.length === 0) {
                possible_new_lines = build_possible_lines_list();
            }*/

            // now pick a random line to add
            var random_new_line = possible_new_lines[Math.floor(Math.random() * possible_new_lines.length)];

            var output = configuration.slice(0);
            output[random_new_line] = 1;

            return output;
        };

        return public_object;
    })();

})(jQuery);<commit_msg>Get rid of palmer methods<commit_after>/* utility package for jsPsych */

(function($) {

    jsPsych.utils = (function() {

        // public methods
        var public_object = {};

        // take the hierarchically arranged jsPsych.data object and turn it into
        // a one-dimensional array where each entry is data from a  single trial
        // append_data is optional and can be items that should be added to all
        // trials, such as a subject identifier
        public_object.flatten_data_object = function(data_object, append_data) {

            append_data = (typeof append_data === undefined) ? {} : append_data;

            var trials = [];

            // loop through data_object
            for (var i = 0; i < data_object.length; i++) {
                for (var j = 0; j < data_object[i].length; j++) {
                    var data = $.extend({}, data_object[i][j], append_data);
                    trials.push(data);
                }
            }

            return trials;
        }

        return public_object;
    })();

})(jQuery);
<|endoftext|>


<commit_before>'use strict';

var Vec2 = require('./vec2.js');

function Boid() {
    var position = Vec2.get();
    var velocity = Vec2.get();
    var steeringForce = Vec2.get();
    var bounds = {
        x: 0,
        y: 0,
        width: 640,
        height: 480
    };
    var edgeBehavior = Boid.EDGE_BOUNCE;
    var mass = 1.0;
    var maxSpeed = 10;
    var maxSpeedSq = maxSpeed * maxSpeed;
    var maxForce = 1;
    // arrive
    var arriveThreshold = 50;
    var arriveThresholdSq = arriveThreshold * arriveThreshold;
    // wander
    var wanderDistance = 10;
    var wanderRadius = 5;
    var wanderAngle = 0;
    var wanderRange = 1;
    // avoid
    var avoidDistance = 300;
    var avoidBuffer = 20;
    // follow path
    var pathIndex = 0;
    var pathThreshold = 20;
    var pathThresholdSq = pathThreshold * pathThreshold;
    // flock
    var maxDistance = 300;
    var maxDistanceSq = maxDistance * maxDistance;
    var minDistance = 60;
    var minDistanceSq = minDistance * minDistance;

    var setBounds = function(width, height, x, y) {
        bounds.width = width;
        bounds.height = height;
        bounds.x = x || 0;
        bounds.y = y || 0;

        return boid;
    };

    var update = function() {
        steeringForce.truncate(maxForce);
        steeringForce.divideBy(mass);
        velocity.add(steeringForce);
        steeringForce.reset();
        velocity.truncate(maxSpeed);
        position.add(velocity);

        if (edgeBehavior === Boid.EDGE_BOUNCE) {
            bounce();
        } else if (edgeBehavior === Boid.EDGE_WRAP) {
            wrap();
        }
        return boid;
    };

    var bounce = function() {
        if (position.x > bounds.width) {
            position.x = bounds.width;
            velocity.x *= -1;
        } else if (position.x < bounds.x) {
            position.x = bounds.x;
            velocity.x *= -1;
        }
        if (position.y > bounds.height) {
            position.y = bounds.height;
            velocity.y *= -1;
        } else if (position.y < bounds.y) {
            position.y = bounds.y;
            velocity.y *= -1;
        }
    };

    var wrap = function() {
        if (position.x > bounds.width) {
            position.x = bounds.x;
        } else if (position.x < bounds.x) {
            position.x = bounds.width;
        }
        if (position.y > bounds.height) {
            position.y = bounds.y;
        } else if (position.y < bounds.y) {
            position.y = bounds.height;
        }
    };

    var seek = function(targetVec) {
        var desiredVelocity = targetVec.clone().subtract(position);
        desiredVelocity.normalize();
        desiredVelocity.scaleBy(maxSpeed);

        var force = desiredVelocity.subtract(velocity);
        steeringForce.add(force);
        force.dispose();

        return boid;
    };

    var flee = function(targetVec) {
        var desiredVelocity = targetVec.clone().subtract(position);
        desiredVelocity.normalize();
        desiredVelocity.scaleBy(maxSpeed);

        var force = desiredVelocity.subtract(velocity);
        steeringForce.subtract(force);
        force.dispose();

        return boid;
    };

    // seek until within arriveThreshold
    var arrive = function(targetVec) {
        var desiredVelocity = targetVec.clone().subtract(position);
        desiredVelocity.normalize();

        var distanceSq = position.distanceSq(targetVec);
        if (distanceSq > arriveThresholdSq) {
            desiredVelocity.scaleBy(maxSpeed);
        } else {
            var scalar = maxSpeed * distanceSq / arriveThresholdSq;
            desiredVelocity.scaleBy(scalar);
        }
        var force = desiredVelocity.subtract(velocity);
        steeringForce.add(force);
        force.dispose();

        return boid;
    };

    // look at velocity of boid and try to predict where it's going
    var pursue = function(targetBoid) {
        var lookAheadTime = position.distanceSq(targetBoid.position) / maxSpeedSq;

        var scaledVelocity = targetBoid.velocity.clone().scaleBy(lookAheadTime);
        var predictedTarget = targetBoid.position.clone().add(scaledVelocity);

        seek(predictedTarget);

        scaledVelocity.dispose();
        predictedTarget.dispose();

        return boid;
    };

    // look at velocity of boid and try to predict where it's going
    var evade = function(targetBoid) {
        var lookAheadTime = position.distanceSq(targetBoid.position) / maxSpeedSq;

        var scaledVelocity = targetBoid.velocity.clone().scaleBy(lookAheadTime);
        var predictedTarget = targetBoid.position.clone().add(scaledVelocity);

        flee(predictedTarget);

        scaledVelocity.dispose();
        predictedTarget.dispose();

        return boid;
    };

    // wander around, changing angle by a limited amount each tick
    var wander = function() {
        var center = velocity.clone().normalize().scaleBy(wanderDistance);

        var offset = Vec2.get();
        offset.length = wanderRadius;
        offset.angle = wanderAngle;
        wanderAngle += Math.random() * wanderRange - wanderRange * 0.5;

        var force = center.add(offset);
        steeringForce.add(force);

        offset.dispose();
        force.dispose();

        return boid;
    };

    // gets a bit rough used in combination with seeking as the boid attempts
    // to seek straight through an object while simultaneously trying to avoid it
    var avoid = function(obstacles) {
        for (var i = 0; i < obstacles.length; i++) {
            var obstacle = obstacles[i];
            var heading = velocity.clone().normalize();

            // vec between obstacle and boid
            var difference = obstacle.position.clone().subtract(position);
            var dotProd = difference.dotProduct(heading);

            // if obstacle in front of boid
            if (dotProd > 0) {
                // vec to represent 'feeler' arm
                var feeler = heading.clone().scaleBy(avoidDistance);
                // project difference onto feeler
                var projection = heading.clone().scaleBy(dotProd);
                // distance from obstacle to feeler
                var vecDistance = projection.subtract(difference);
                var distance = vecDistance.length;
                // if feeler intersects obstacle (plus buffer), and projection
                // less than feeler length, will collide
                if (distance < (obstacle.radius || 0) + avoidBuffer && projection.length < feeler.length) {
                    // calc a force +/- 90 deg from vec to circ
                    var force = heading.clone().scaleBy(maxSpeed);
                    force.angle += difference.sign(velocity) * Math.PI / 2;
                    // scale force by distance (further = smaller force)
                    force.scaleBy(1 - projection.length / feeler.length);
                    // add to steering force
                    steeringForce.add(force);
                    // braking force - slows boid down so it has time to turn (closer = harder)
                    velocity.scaleBy(projection.length / feeler.length);

                    force.dispose();
                }
                feeler.dispose();
                projection.dispose();
                vecDistance.dispose();
            }
            heading.dispose();
            difference.dispose();
        }
        return boid;
    };

    // follow a path made up of an array or vectors
    var followPath = function(path, loop) {
        loop = !!loop;

        var wayPoint = path[pathIndex];
        if (!wayPoint) {
            pathIndex = 0;
            return boid;
        }
        if (position.distanceSq(wayPoint) < pathThresholdSq) {
            if (pathIndex >= path.length - 1) {
                if (loop) {
                    pathIndex = 0;
                }
            } else {
                pathIndex++;
            }
        }
        if (pathIndex >= path.length - 1 && !loop) {
            arrive(wayPoint);
        } else {
            seek(wayPoint);
        }
        return boid;
    };

    // flock - group of boids loosely move together
    var flock = function(boids) {
        var averageVelocity = velocity.clone();
        var averagePosition = Vec2.get();
        var inSightCount = 0;
        for (var i = 0; i < boids.length; i++) {
            var b = boids[i];
            if (b !== boid && inSight(b)) {
                averageVelocity.add(b.velocity);
                averagePosition.add(b.position);
                if (tooClose(b)) {
                    flee(b.position);
                }
                inSightCount++;
            }
        }
        if (inSightCount > 0) {
            averageVelocity.divideBy(inSightCount);
            averagePosition.divideBy(inSightCount);
            seek(averagePosition);
            steeringForce.add(averageVelocity.subtract(velocity));
        }
        averageVelocity.dispose();
        averagePosition.dispose();

        return boid;
    };

    // is boid close enough to be in sight and facing
    var inSight = function(boid) {
        if (position.distanceSq(boid.position) > maxDistanceSq) {
            return false;
        }
        var heading = velocity.clone().normalize();
        var difference = boid.position.clone().subtract(position);
        var dotProd = difference.dotProduct(heading);

        heading.dispose();
        difference.dispose();

        if (dotProd < 0) {
            return false;
        }
        return true;
    };

    // is boid too close?
    var tooClose = function(boid) {
        return position.distanceSq(boid.position) < minDistanceSq;
    };

    // methods
    var boid = {
        setBounds: setBounds,
        update: update,
        pursue: pursue,
        evade: evade,
        wander: wander,
        avoid: avoid,
        followPath: followPath,
        flock: flock,
        arrive: arrive,
        flee: flee,
        position: position,
        velocity: velocity,
        userData: {}
    };

    // getters / setters
    Object.defineProperties(boid, {
        edgeBehavior: {
            get: function() {
                return edgeBehavior;
            },
            set: function(value) {
                edgeBehavior = value;
            }
        },
        mass: {
            get: function() {
                return mass;
            },
            set: function(value) {
                mass = value;
            }
        },
        maxSpeed: {
            get: function() {
                return maxSpeed;
            },
            set: function(value) {
                maxSpeed = value;
                maxSpeedSq = value * value;
            }
        },
        maxForce: {
            get: function() {
                return maxForce;
            },
            set: function(value) {
                maxForce = value;
            }
        },
        // arrive
        arriveThreshold: {
            get: function() {
                return arriveThreshold;
            },
            set: function(value) {
                arriveThreshold = value;
                arriveThresholdSq = value * value;
            }
        },
        // wander
        wanderDistance: {
            get: function() {
                return wanderDistance;
            },
            set: function(value) {
                wanderDistance = value;
            }
        },
        wanderRadius: {
            get: function() {
                return wanderRadius;
            },
            set: function(value) {
                wanderRadius = value;
            }
        },
        wanderRange: {
            get: function() {
                return wanderRange;
            },
            set: function(value) {
                wanderRange = value;
            }
        },
        // avoid
        avoidDistance: {
            get: function() {
                return avoidDistance;
            },
            set: function(value) {
                avoidDistance = value;
            }
        },
        avoidBuffer: {
            get: function() {
                return avoidBuffer;
            },
            set: function(value) {
                avoidBuffer = value;
            }
        },
        // followPath
        pathIndex: {
            get: function() {
                return pathIndex;
            },
            set: function(value) {
                pathIndex = value;
            }
        },
        pathThreshold: {
            get: function() {
                return pathThreshold;
            },
            set: function(value) {
                pathThreshold = value;
                pathThresholdSq = value * value;
            }
        },
        //  flock
        maxDistance: {
            get: function() {
                return maxDistance;
            },
            set: function(value) {
                maxDistance = value;
                maxDistanceSq = value * value;
            }
        },
        minDistance: {
            get: function() {
                return minDistance;
            },
            set: function(value) {
                minDistance = value;
                minDistanceSq = value * value;
            }
        }
    });

    return Object.freeze(boid);
}

// edge behaviors
Boid.EDGE_NONE = 'none';
Boid.EDGE_BOUNCE = 'bounce';
Boid.EDGE_WRAP = 'wrap';

// vec2
Boid.Vec2 = Vec2;

Boid.vec2 = function(x, y) {
    return Vec2.get(x, y);
};

// for defining obstacles or areas to avoid
Boid.obstacle = function(radius, x, y) {
    return {
        radius: radius,
        position: Vec2.get(x, y)
    };
};

if (typeof module === 'object' && module.exports) {
    module.exports = Boid;
}
<commit_msg>Makes boids configurable by options object<commit_after>'use strict';

var Vec2 = require('./vec2.js');

var defaults = {
    bounds: {
        x: 0,
        y: 0,
        width: 640,
        height: 480
    },
    edgeBehavior: 'bounce',
    mass: 1.0,
    maxSpeed: 10,
    maxForce: 1,
    arriveThreshold: 50,
    wanderDistance: 10,
    wanderRadius: 5,
    wanderAngle: 0,
    wanderRange: 1,
    avoidDistance: 300,
    avoidBuffer: 20,
    pathThreshold: 20,
    maxDistance: 300,
    minDistance: 60
};

function Boid(options) {
    options = configure(options);

    var position = Vec2.get();
    var velocity = Vec2.get();
    var steeringForce = Vec2.get();

    var bounds = options.bounds;
    var edgeBehavior = options.edgeBehavior;
    var mass = options.mass;
    var maxSpeed = options.maxSpeed;
    var maxSpeedSq = maxSpeed * maxSpeed;
    var maxForce = options.maxForce;
    // arrive
    var arriveThreshold = options.arriveThreshold;
    var arriveThresholdSq = arriveThreshold * arriveThreshold;
    // wander
    var wanderDistance = options.wanderDistance;
    var wanderRadius = options.wanderRadius;
    var wanderAngle = options.wanderAngle;
    var wanderRange = options.wanderRange;
    // avoid
    var avoidDistance = options.avoidDistance;
    var avoidBuffer = options.avoidBuffer;
    // follow path
    var pathIndex = 0;
    var pathThreshold = options.pathThreshold;
    var pathThresholdSq = pathThreshold * pathThreshold;
    // flock
    var maxDistance = options.maxDistance;
    var maxDistanceSq = maxDistance * maxDistance;
    var minDistance = options.minDistance;
    var minDistanceSq = minDistance * minDistance;

    var setBounds = function(width, height, x, y) {
        bounds.width = width;
        bounds.height = height;
        bounds.x = x || 0;
        bounds.y = y || 0;

        return boid;
    };

    var update = function() {
        steeringForce.truncate(maxForce);
        steeringForce.divideBy(mass);
        velocity.add(steeringForce);
        steeringForce.reset();
        velocity.truncate(maxSpeed);
        position.add(velocity);

        if (edgeBehavior === Boid.EDGE_BOUNCE) {
            bounce();
        } else if (edgeBehavior === Boid.EDGE_WRAP) {
            wrap();
        }
        return boid;
    };

    var bounce = function() {
        if (position.x > bounds.width) {
            position.x = bounds.width;
            velocity.x *= -1;
        } else if (position.x < bounds.x) {
            position.x = bounds.x;
            velocity.x *= -1;
        }
        if (position.y > bounds.height) {
            position.y = bounds.height;
            velocity.y *= -1;
        } else if (position.y < bounds.y) {
            position.y = bounds.y;
            velocity.y *= -1;
        }
    };

    var wrap = function() {
        if (position.x > bounds.width) {
            position.x = bounds.x;
        } else if (position.x < bounds.x) {
            position.x = bounds.width;
        }
        if (position.y > bounds.height) {
            position.y = bounds.y;
        } else if (position.y < bounds.y) {
            position.y = bounds.height;
        }
    };

    var seek = function(targetVec) {
        var desiredVelocity = targetVec.clone().subtract(position);
        desiredVelocity.normalize();
        desiredVelocity.scaleBy(maxSpeed);

        var force = desiredVelocity.subtract(velocity);
        steeringForce.add(force);
        force.dispose();

        return boid;
    };

    var flee = function(targetVec) {
        var desiredVelocity = targetVec.clone().subtract(position);
        desiredVelocity.normalize();
        desiredVelocity.scaleBy(maxSpeed);

        var force = desiredVelocity.subtract(velocity);
        steeringForce.subtract(force);
        force.dispose();

        return boid;
    };

    // seek until within arriveThreshold
    var arrive = function(targetVec) {
        var desiredVelocity = targetVec.clone().subtract(position);
        desiredVelocity.normalize();

        var distanceSq = position.distanceSq(targetVec);
        if (distanceSq > arriveThresholdSq) {
            desiredVelocity.scaleBy(maxSpeed);
        } else {
            var scalar = maxSpeed * distanceSq / arriveThresholdSq;
            desiredVelocity.scaleBy(scalar);
        }
        var force = desiredVelocity.subtract(velocity);
        steeringForce.add(force);
        force.dispose();

        return boid;
    };

    // look at velocity of boid and try to predict where it's going
    var pursue = function(targetBoid) {
        var lookAheadTime = position.distanceSq(targetBoid.position) / maxSpeedSq;

        var scaledVelocity = targetBoid.velocity.clone().scaleBy(lookAheadTime);
        var predictedTarget = targetBoid.position.clone().add(scaledVelocity);

        seek(predictedTarget);

        scaledVelocity.dispose();
        predictedTarget.dispose();

        return boid;
    };

    // look at velocity of boid and try to predict where it's going
    var evade = function(targetBoid) {
        var lookAheadTime = position.distanceSq(targetBoid.position) / maxSpeedSq;

        var scaledVelocity = targetBoid.velocity.clone().scaleBy(lookAheadTime);
        var predictedTarget = targetBoid.position.clone().add(scaledVelocity);

        flee(predictedTarget);

        scaledVelocity.dispose();
        predictedTarget.dispose();

        return boid;
    };

    // wander around, changing angle by a limited amount each tick
    var wander = function() {
        var center = velocity.clone().normalize().scaleBy(wanderDistance);

        var offset = Vec2.get();
        offset.length = wanderRadius;
        offset.angle = wanderAngle;
        wanderAngle += Math.random() * wanderRange - wanderRange * 0.5;

        var force = center.add(offset);
        steeringForce.add(force);

        offset.dispose();
        force.dispose();

        return boid;
    };

    // gets a bit rough used in combination with seeking as the boid attempts
    // to seek straight through an object while simultaneously trying to avoid it
    var avoid = function(obstacles) {
        for (var i = 0; i < obstacles.length; i++) {
            var obstacle = obstacles[i];
            var heading = velocity.clone().normalize();

            // vec between obstacle and boid
            var difference = obstacle.position.clone().subtract(position);
            var dotProd = difference.dotProduct(heading);

            // if obstacle in front of boid
            if (dotProd > 0) {
                // vec to represent 'feeler' arm
                var feeler = heading.clone().scaleBy(avoidDistance);
                // project difference onto feeler
                var projection = heading.clone().scaleBy(dotProd);
                // distance from obstacle to feeler
                var vecDistance = projection.subtract(difference);
                var distance = vecDistance.length;
                // if feeler intersects obstacle (plus buffer), and projection
                // less than feeler length, will collide
                if (distance < (obstacle.radius || 0) + avoidBuffer && projection.length < feeler.length) {
                    // calc a force +/- 90 deg from vec to circ
                    var force = heading.clone().scaleBy(maxSpeed);
                    force.angle += difference.sign(velocity) * Math.PI / 2;
                    // scale force by distance (further = smaller force)
                    force.scaleBy(1 - projection.length / feeler.length);
                    // add to steering force
                    steeringForce.add(force);
                    // braking force - slows boid down so it has time to turn (closer = harder)
                    velocity.scaleBy(projection.length / feeler.length);

                    force.dispose();
                }
                feeler.dispose();
                projection.dispose();
                vecDistance.dispose();
            }
            heading.dispose();
            difference.dispose();
        }
        return boid;
    };

    // follow a path made up of an array or vectors
    var followPath = function(path, loop) {
        loop = !!loop;

        var wayPoint = path[pathIndex];
        if (!wayPoint) {
            pathIndex = 0;
            return boid;
        }
        if (position.distanceSq(wayPoint) < pathThresholdSq) {
            if (pathIndex >= path.length - 1) {
                if (loop) {
                    pathIndex = 0;
                }
            } else {
                pathIndex++;
            }
        }
        if (pathIndex >= path.length - 1 && !loop) {
            arrive(wayPoint);
        } else {
            seek(wayPoint);
        }
        return boid;
    };

    // flock - group of boids loosely move together
    var flock = function(boids) {
        var averageVelocity = velocity.clone();
        var averagePosition = Vec2.get();
        var inSightCount = 0;
        for (var i = 0; i < boids.length; i++) {
            var b = boids[i];
            if (b !== boid && inSight(b)) {
                averageVelocity.add(b.velocity);
                averagePosition.add(b.position);
                if (tooClose(b)) {
                    flee(b.position);
                }
                inSightCount++;
            }
        }
        if (inSightCount > 0) {
            averageVelocity.divideBy(inSightCount);
            averagePosition.divideBy(inSightCount);
            seek(averagePosition);
            steeringForce.add(averageVelocity.subtract(velocity));
        }
        averageVelocity.dispose();
        averagePosition.dispose();

        return boid;
    };

    // is boid close enough to be in sight and facing
    var inSight = function(boid) {
        if (position.distanceSq(boid.position) > maxDistanceSq) {
            return false;
        }
        var heading = velocity.clone().normalize();
        var difference = boid.position.clone().subtract(position);
        var dotProd = difference.dotProduct(heading);

        heading.dispose();
        difference.dispose();

        if (dotProd < 0) {
            return false;
        }
        return true;
    };

    // is boid too close?
    var tooClose = function(boid) {
        return position.distanceSq(boid.position) < minDistanceSq;
    };

    // methods
    var boid = {
        setBounds: setBounds,
        update: update,
        pursue: pursue,
        evade: evade,
        wander: wander,
        avoid: avoid,
        followPath: followPath,
        flock: flock,
        arrive: arrive,
        flee: flee,
        position: position,
        velocity: velocity,
        userData: {}
    };

    // getters / setters
    Object.defineProperties(boid, {
        edgeBehavior: {
            get: function() {
                return edgeBehavior;
            },
            set: function(value) {
                edgeBehavior = value;
            }
        },
        mass: {
            get: function() {
                return mass;
            },
            set: function(value) {
                mass = value;
            }
        },
        maxSpeed: {
            get: function() {
                return maxSpeed;
            },
            set: function(value) {
                maxSpeed = value;
                maxSpeedSq = value * value;
            }
        },
        maxForce: {
            get: function() {
                return maxForce;
            },
            set: function(value) {
                maxForce = value;
            }
        },
        // arrive
        arriveThreshold: {
            get: function() {
                return arriveThreshold;
            },
            set: function(value) {
                arriveThreshold = value;
                arriveThresholdSq = value * value;
            }
        },
        // wander
        wanderDistance: {
            get: function() {
                return wanderDistance;
            },
            set: function(value) {
                wanderDistance = value;
            }
        },
        wanderRadius: {
            get: function() {
                return wanderRadius;
            },
            set: function(value) {
                wanderRadius = value;
            }
        },
        wanderRange: {
            get: function() {
                return wanderRange;
            },
            set: function(value) {
                wanderRange = value;
            }
        },
        // avoid
        avoidDistance: {
            get: function() {
                return avoidDistance;
            },
            set: function(value) {
                avoidDistance = value;
            }
        },
        avoidBuffer: {
            get: function() {
                return avoidBuffer;
            },
            set: function(value) {
                avoidBuffer = value;
            }
        },
        // followPath
        pathIndex: {
            get: function() {
                return pathIndex;
            },
            set: function(value) {
                pathIndex = value;
            }
        },
        pathThreshold: {
            get: function() {
                return pathThreshold;
            },
            set: function(value) {
                pathThreshold = value;
                pathThresholdSq = value * value;
            }
        },
        //  flock
        maxDistance: {
            get: function() {
                return maxDistance;
            },
            set: function(value) {
                maxDistance = value;
                maxDistanceSq = value * value;
            }
        },
        minDistance: {
            get: function() {
                return minDistance;
            },
            set: function(value) {
                minDistance = value;
                minDistanceSq = value * value;
            }
        }
    });

    return Object.freeze(boid);
}

// edge behaviors
Boid.EDGE_NONE = 'none';
Boid.EDGE_BOUNCE = 'bounce';
Boid.EDGE_WRAP = 'wrap';

// vec2
Boid.Vec2 = Vec2;

Boid.vec2 = function(x, y) {
    return Vec2.get(x, y);
};

// for defining obstacles or areas to avoid
Boid.obstacle = function(radius, x, y) {
    return {
        radius: radius,
        position: Vec2.get(x, y)
    };
};

function setDefaults(opts, defs) {
    Object.keys(defs).forEach(function(key) {
        if (typeof opts[key] === 'undefined') {
            opts[key] = defs[key];
        }
    });
}

function configure(options) {
    options = options || {};
    options.bounds = options.bounds || {};
    setDefaults(options, defaults);
    setDefaults(options.bounds, defaults.bounds);
    return options;
}

if (typeof module === 'object' && module.exports) {
    module.exports = Boid;
}
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
"use strict";
/*global gpfSourcesPath:true*/ // Global source path
/*exported _GPF_HOST_BROWSER*/
/*exported _GPF_HOST_NODEJS*/
/*exported _GPF_HOST_PHANTOMJS*/
/*exported _GPF_HOST_UNKNOWN*/
/*exported _GPF_HOST_WSCRIPT*/
/*exported _gpfContext*/
/*exported _gpfDosPath*/
/*exported _gpfEmptyFunc*/
/*exported _gpfExit*/
/*exported _gpfHost*/
/*exported _gpfIgnore*/
/*exported _gpfInBrowser*/
/*exported _gpfInNode*/
/*exported _gpfMainContext*/
/*exported _gpfMsFSO*/
/*exported _gpfNodeFs*/
/*exported _gpfNodePath*/
/*exported _gpfResolveScope*/
/*exported _gpfVersion*/
/*exported _gpfWebDocument*/
/*exported _gpfWebHead*/
/*exported _gpfWebWindow*/
/*#endif*/

/*global phantom:true*/
/*jshint browser: true*/
/*jshint node: true*/
/*jshint wsh: true*/
/*global WScript*/

var
    // GPF version
    _gpfVersion = "0.1",

    // Host type constants
    _GPF_HOST_BROWSER               = "browser",
    _GPF_HOST_NODEJS                = "nodejs",
    _GPF_HOST_PHANTOMJS             = "phantomjs",
    _GPF_HOST_UNKNOWN               = "unknown",
    _GPF_HOST_WSCRIPT               = "wscript",

    // Host type, see _GPF_HOST_xxx
    _gpfHost = _GPF_HOST_UNKNOWN,

    // Indicates that paths are DOS-like (i.e. case insensitive with /)
    _gpfDosPath = false,

    /*jshint -W040*/ // This is the common way to get the global context
    // Main context object
    _gpfMainContext = this,
    /*jshint +W040*/

    /**
     * To implement gpf.noConflict(), we need to keep the previous content of gpf.
     * Makes sense only for the following hosts:
     * - phantomjs
     * - browser
     * - unknown
     *
     * @type {undefined|Object}
     */
    _gpfConflictingSymbol,

    // An empty function
    _gpfEmptyFunc = function () {},

    /**
     * Helper to ignore unused parameter
     *
     * @param {*} param
     */
    /*gpf:nop*/ _gpfIgnore = _gpfEmptyFunc,

    /**
     * Exit function
     *
     * @param {Number} code
     */
    _gpfExit = _gpfEmptyFunc,

    /**
     * Translate the parameter into a valid scope
     *
     * @param {*} scope
     */
    _gpfResolveScope = function (scope) {
        if (null === scope || "object" !== typeof scope) {
            return _gpfMainContext;
        }
        return scope;
    },

    // The current host is a nodeJS like
    _gpfInNode = false,

    // The current host is a browser like
    _gpfInBrowser = false,

    /**
     * Browser window object
     *
     * @type {Object}
     */
    _gpfWebWindow,

    /**
     * Browser document object
     *
     * @type {Object}
     */
     _gpfWebDocument,

    /**
     * Browser head tag
     *
     * @type {Object}
     */
    _gpfWebHead,

    /**
     * Scripting.FileSystemObject activeX
     *
     * @type {Object}
     */
    _gpfMsFSO,

    /**
     * Node require("fs")
     *
     * @type {Object}
     */
    _gpfNodeFs,

    /**
     * Node require("path")
     *
     * @type {Object}
     */
    _gpfNodePath;

/*#ifdef(DEBUG)*/

_gpfVersion += "d";

/*#endif*/

// Microsoft cscript / wscript
if ("undefined" !== typeof WScript) {
    _gpfHost = _GPF_HOST_WSCRIPT;
    _gpfDosPath = true;
    _gpfMainContext = (function () {return this;}).apply(null, []);
    _gpfExit = function (code) {
        WScript.Quit(code);
    };

    // Define console APIs
    _gpfMainContext.console = {
        log: function (t) {WScript.Echo("    " + t);},
        info: function (t) {WScript.Echo("[?] " + t);},
        warn: function (t) {WScript.Echo("/!\\ " + t);},
        error: function (t) {WScript.Echo("(X) " + t);}
    };

// PhantomJS
} else if ("undefined" !== typeof phantom && phantom.version) {
    _gpfHost = _GPF_HOST_PHANTOMJS;
    _gpfDosPath = require("fs").separator === "\\";
    _gpfMainContext = window;
    _gpfInNode = true;
    _gpfInBrowser = true;
    _gpfExit = phantom.exit;

// Nodejs
} else if ("undefined" !== typeof module && module.exports) {
    _gpfHost = _GPF_HOST_NODEJS;
    _gpfNodePath = require("path");
    _gpfDosPath = _gpfNodePath.sep === "\\";
    _gpfMainContext = global;
    _gpfInNode = true;
    _gpfExit = process.exit;

// Browser
} else if ("undefined" !== typeof window) {
    _gpfHost = _GPF_HOST_BROWSER;
    _gpfMainContext = window;
    _gpfInBrowser = true;
    _gpfExit = _gpfEmptyFunc;
    _gpfWebWindow = window;
    _gpfWebDocument = document;
    _gpfWebHead = _gpfWebDocument.getElementsByTagName("head")[0] || _gpfWebDocument.documentElement;

}

/*#ifndef(UMD)*/

_gpfMainContext.gpf = {};

/*#else*/

_gpfConflictingSymbol = _gpfMainContext.gpf;

/**
 * Relinquish control of the gpf variable.
 *
 * @return {Object} current GPF instance
 */
gpf.noConflict = function () {
    if (undefined !== _gpfConflictingSymbol) {
        _gpfMainContext.gpf = _gpfConflictingSymbol;
    } else {
        delete _gpfMainContext.gpf;
    }
    return gpf;
};

/*#endif*/

// Install host specifics (if any)
if (_gpfInNode) {
    gpf.node = {};
}
// Some web-related tools will be configured even if not in a browser, declare the namespace now
gpf.web = {};

// Returns the current version
gpf.version = function () {
    return _gpfVersion;
};

/**
 * Returns a string identifying the detected host
 *
 * @return {String}
 * - gpf.HOST_WSCRIPT for cscript and wscript
 * - gpf.HOST_NODEJS for nodejs
 * - gpf.HOST_PHANTOMJS for phantomjs
 * - gpf.HOST_BROWSER for any browser
 * - gpf.HOST_UNKNOWN if not detected
 */
gpf.host = function () {
    return _gpfHost;
};

function _getObjectProperty(parent, name) {
    if (undefined !== parent) {
        return parent[name];
    }
}

function _getOrCreateObjectProperty(parent, name) {
    var result = parent[name];
    if (undefined === result) {
        result = parent[name] = {};
    }
    return result;
}

/**
 * Resolve the provided contextual path and returns the result
 *
 * @param {String[]} path array of identifiers
 * @param {Boolean} [createMissingParts=false] createMissingParts if the path leads to undefined parts and
 * createMissingParts is true, it allocates a default empty object
 *
 * @return {*|undefined}
 * - when path is undefined, it returns the current host higher object
 * - when path is "gpf" it returns the GPF object
 */
function _gpfContext (path, createMissingParts) {
    var reducer,
        rootContext;
    if (createMissingParts) {
        reducer = _getOrCreateObjectProperty;
    } else {
        reducer = _getObjectProperty;
    }
    if (path[0] === "gpf") {
        rootContext = gpf;
        path = path.splice(1);
    } else {
        rootContext = _gpfMainContext;
    }
    return path.reduce(reducer, rootContext);
}

/**
 * Resolve the provided contextual path and returns the result
 *
 * @param {String} path Dot separated list of identifiers
 *
 * @return {*|undefined}
 * - when path is undefined, it returns the current host higher object
 * - when path is "gpf" it returns the GPF object
 */
gpf.context = function (path) {
    if (undefined === path) {
        return _gpfMainContext;
    }
    return _gpfContext(path.split("."));
};

/*#ifndef(UMD)*/

/*exported _gpfFinishLoading*/ // Will be used by the boot specifics

var
    // Set to true once all sources of GPF are loaded
    _gpfLoaded = false,

    /**
     * List of functions to call on load
     *
     * @type {Function[]}
     */
    _gpfLoadCallbacks = [],

    // Callback used once all sources are loaded, invoke the callbacks
    _gpfFinishLoading = function () {
        _gpfLoaded = true;
        while (_gpfLoadCallbacks.length) {
            _gpfLoadCallbacks.shift()();
        }
    };

/**
 * Test if GPF is loaded
 *
 * @param {Function} [callback=undefined] callback This callback is invoked when GPF is loaded (may be immediately if
 * already loaded)
 * @return {Boolean} True if GPF is fully loaded, false otherwise
 */
gpf.loaded = function (callback) {
    if (callback) {
        if (_gpfLoaded) {
            callback();
        } else {
            _gpfLoadCallbacks.push(callback);
        }
    }
    return _gpfLoaded;
};

if(!gpf.loaded) {

/*#else*/

    gpf.loaded = function (callback) {
        if (callback) {
            callback();
        }
        return true;
    };

/*#endif*/

/*#ifndef(UMD)*/

}

/*#endif*/

/*#ifdef(DEBUG)*/

// DEBUG specifics

/**
 * Assertion helper
 *
 * @param {Boolean} condition May be a truthy value
 * @param {String} message Assertion message (to explain the violation if it fails)
 */
gpf.ASSERT = function (condition, message) {
    if (undefined === message) {
        message = "gpf.ASSERT with no message";
        condition = false;
    }
    if (!condition) {
        console.warn("ASSERTION FAILED: " + message);
        throw gpf.Error.AssertionFailed({
            message: message
        });
    }
};

/**
 * Batch assertion helper
 *
 * @param {Object} messages Dictionary of messages (value being the condition)
 */
gpf.ASSERTS = function (messages) {
    for (var message in messages) {
        if (messages.hasOwnProperty(message)) {
            gpf.ASSERT(messages[message], message);
        }
    }
};

if (!gpf.ASSERT) {

/*#else*/

    /*gpf:nop*/ gpf.ASSERT = _gpfEmptyFunc;
    /*gpf:nop*/ gpf.ASSERTS = _gpfEmptyFunc;

/*#endif*/

/*#ifdef(DEBUG)*/

}

/*#endif*/

/*#ifndef(UMD)*/

/**
 * Loading sources occurs here for the non UMD version.
 * UMD versions (debug / release) will have everything concatenated.
 */

// gpfSourcesPath - if defined - gives the relative path to sources
if ("undefined" === typeof gpfSourcesPath) {
    _gpfMainContext.gpfSourcesPath = "";
} else {
    var pathSep;
    if (_gpfDosPath) {
        pathSep = "\\";
    } else {
        pathSep = "/";
    }
    if (gpfSourcesPath.charAt(gpfSourcesPath.length - 1) !== pathSep) {
        gpfSourcesPath = gpfSourcesPath + pathSep;
    }
}

if (_GPF_HOST_WSCRIPT === _gpfHost) {
    _gpfMsFSO = new ActiveXObject("Scripting.FileSystemObject");
    (function () {
        var srcFile,
            srcContent;
        srcFile = _gpfMsFSO.OpenTextFile(gpfSourcesPath + "boot_ms.js");
        srcContent = srcFile.ReadAll();
        srcFile.Close();
        /*jslint evil: true*/
        eval(srcContent);
        /*jslint evil: false*/
    }());

} else if (_gpfInNode) {
    _gpfNodeFs =  require("fs");

    /**
     * Phantom/Node File System read text file method (boot)
     * @type {Function}
     */
    var _gpfFSRead;
    if (_GPF_HOST_PHANTOMJS === _gpfHost) {
        _gpfFSRead = _gpfNodeFs.read;
    } else {
        _gpfFSRead = function (path) {
            return _gpfNodeFs.readFileSync(path).toString();
        };
    }
    /*jslint evil: true*/
    eval(_gpfFSRead(gpfSourcesPath + "boot_node.js"));
    /*jslint evil: false*/

} else { // _GPF_HOST_BROWSER === _gpfHost
    var _gpfWebRawInclude = function (src) {
        var script = _gpfWebDocument.createElement("script");
        script.language = "javascript";
        script.src = gpfSourcesPath + src;
        _gpfWebHead.insertBefore(script, _gpfWebHead.firstChild);
    };
    _gpfWebRawInclude("boot_web.js");

}

/*#endif*/
<commit_msg>Bug fix on _gpfContext<commit_after>/*#ifndef(UMD)*/
"use strict";
/*global gpfSourcesPath:true*/ // Global source path
/*exported _GPF_HOST_BROWSER*/
/*exported _GPF_HOST_NODEJS*/
/*exported _GPF_HOST_PHANTOMJS*/
/*exported _GPF_HOST_UNKNOWN*/
/*exported _GPF_HOST_WSCRIPT*/
/*exported _gpfContext*/
/*exported _gpfDosPath*/
/*exported _gpfEmptyFunc*/
/*exported _gpfExit*/
/*exported _gpfHost*/
/*exported _gpfIgnore*/
/*exported _gpfInBrowser*/
/*exported _gpfInNode*/
/*exported _gpfMainContext*/
/*exported _gpfMsFSO*/
/*exported _gpfNodeFs*/
/*exported _gpfNodePath*/
/*exported _gpfResolveScope*/
/*exported _gpfVersion*/
/*exported _gpfWebDocument*/
/*exported _gpfWebHead*/
/*exported _gpfWebWindow*/
/*#endif*/

/*global phantom:true*/
/*jshint browser: true*/
/*jshint node: true*/
/*jshint wsh: true*/
/*global WScript*/

var
    // GPF version
    _gpfVersion = "0.1",

    // Host type constants
    _GPF_HOST_BROWSER               = "browser",
    _GPF_HOST_NODEJS                = "nodejs",
    _GPF_HOST_PHANTOMJS             = "phantomjs",
    _GPF_HOST_UNKNOWN               = "unknown",
    _GPF_HOST_WSCRIPT               = "wscript",

    // Host type, see _GPF_HOST_xxx
    _gpfHost = _GPF_HOST_UNKNOWN,

    // Indicates that paths are DOS-like (i.e. case insensitive with /)
    _gpfDosPath = false,

    /*jshint -W040*/ // This is the common way to get the global context
    // Main context object
    _gpfMainContext = this,
    /*jshint +W040*/

    /**
     * To implement gpf.noConflict(), we need to keep the previous content of gpf.
     * Makes sense only for the following hosts:
     * - phantomjs
     * - browser
     * - unknown
     *
     * @type {undefined|Object}
     */
    _gpfConflictingSymbol,

    // An empty function
    _gpfEmptyFunc = function () {},

    /**
     * Helper to ignore unused parameter
     *
     * @param {*} param
     */
    /*gpf:nop*/ _gpfIgnore = _gpfEmptyFunc,

    /**
     * Exit function
     *
     * @param {Number} code
     */
    _gpfExit = _gpfEmptyFunc,

    /**
     * Translate the parameter into a valid scope
     *
     * @param {*} scope
     */
    _gpfResolveScope = function (scope) {
        if (null === scope || "object" !== typeof scope) {
            return _gpfMainContext;
        }
        return scope;
    },

    // The current host is a nodeJS like
    _gpfInNode = false,

    // The current host is a browser like
    _gpfInBrowser = false,

    /**
     * Browser window object
     *
     * @type {Object}
     */
    _gpfWebWindow,

    /**
     * Browser document object
     *
     * @type {Object}
     */
     _gpfWebDocument,

    /**
     * Browser head tag
     *
     * @type {Object}
     */
    _gpfWebHead,

    /**
     * Scripting.FileSystemObject activeX
     *
     * @type {Object}
     */
    _gpfMsFSO,

    /**
     * Node require("fs")
     *
     * @type {Object}
     */
    _gpfNodeFs,

    /**
     * Node require("path")
     *
     * @type {Object}
     */
    _gpfNodePath;

/*#ifdef(DEBUG)*/

_gpfVersion += "d";

/*#endif*/

// Microsoft cscript / wscript
if ("undefined" !== typeof WScript) {
    _gpfHost = _GPF_HOST_WSCRIPT;
    _gpfDosPath = true;
    _gpfMainContext = (function () {return this;}).apply(null, []);
    _gpfExit = function (code) {
        WScript.Quit(code);
    };

    // Define console APIs
    _gpfMainContext.console = {
        log: function (t) {WScript.Echo("    " + t);},
        info: function (t) {WScript.Echo("[?] " + t);},
        warn: function (t) {WScript.Echo("/!\\ " + t);},
        error: function (t) {WScript.Echo("(X) " + t);}
    };

// PhantomJS
} else if ("undefined" !== typeof phantom && phantom.version) {
    _gpfHost = _GPF_HOST_PHANTOMJS;
    _gpfDosPath = require("fs").separator === "\\";
    _gpfMainContext = window;
    _gpfInNode = true;
    _gpfInBrowser = true;
    _gpfExit = phantom.exit;

// Nodejs
} else if ("undefined" !== typeof module && module.exports) {
    _gpfHost = _GPF_HOST_NODEJS;
    _gpfNodePath = require("path");
    _gpfDosPath = _gpfNodePath.sep === "\\";
    _gpfMainContext = global;
    _gpfInNode = true;
    _gpfExit = process.exit;

// Browser
} else if ("undefined" !== typeof window) {
    _gpfHost = _GPF_HOST_BROWSER;
    _gpfMainContext = window;
    _gpfInBrowser = true;
    _gpfExit = _gpfEmptyFunc;
    _gpfWebWindow = window;
    _gpfWebDocument = document;
    _gpfWebHead = _gpfWebDocument.getElementsByTagName("head")[0] || _gpfWebDocument.documentElement;

}

/*#ifndef(UMD)*/

_gpfMainContext.gpf = {};

/*#else*/

_gpfConflictingSymbol = _gpfMainContext.gpf;

/**
 * Relinquish control of the gpf variable.
 *
 * @return {Object} current GPF instance
 */
gpf.noConflict = function () {
    if (undefined !== _gpfConflictingSymbol) {
        _gpfMainContext.gpf = _gpfConflictingSymbol;
    } else {
        delete _gpfMainContext.gpf;
    }
    return gpf;
};

/*#endif*/

// Install host specifics (if any)
if (_gpfInNode) {
    gpf.node = {};
}
// Some web-related tools will be configured even if not in a browser, declare the namespace now
gpf.web = {};

// Returns the current version
gpf.version = function () {
    return _gpfVersion;
};

/**
 * Returns a string identifying the detected host
 *
 * @return {String}
 * - gpf.HOST_WSCRIPT for cscript and wscript
 * - gpf.HOST_NODEJS for nodejs
 * - gpf.HOST_PHANTOMJS for phantomjs
 * - gpf.HOST_BROWSER for any browser
 * - gpf.HOST_UNKNOWN if not detected
 */
gpf.host = function () {
    return _gpfHost;
};

function _getObjectProperty(parent, name) {
    if (undefined !== parent) {
        return parent[name];
    }
}

function _getOrCreateObjectProperty(parent, name) {
    var result = parent[name];
    if (undefined === result) {
        result = parent[name] = {};
    }
    return result;
}

/**
 * Resolve the provided contextual path and returns the result
 *
 * @param {String[]} path array of identifiers
 * @param {Boolean} [createMissingParts=false] createMissingParts if the path leads to undefined parts and
 * createMissingParts is true, it allocates a default empty object
 *
 * @return {*|undefined}
 * - when path is undefined, it returns the current host higher object
 * - when path is "gpf" it returns the GPF object
 */
function _gpfContext (path, createMissingParts) {
    var reducer,
        rootContext;
    if (createMissingParts) {
        reducer = _getOrCreateObjectProperty;
    } else {
        reducer = _getObjectProperty;
    }
    if (path[0] === "gpf") {
        rootContext = gpf;
        path = path.slice(1);
    } else {
        rootContext = _gpfMainContext;
    }
    return path.reduce(reducer, rootContext);
}

/**
 * Resolve the provided contextual path and returns the result
 *
 * @param {String} path Dot separated list of identifiers
 *
 * @return {*|undefined}
 * - when path is undefined, it returns the current host higher object
 * - when path is "gpf" it returns the GPF object
 */
gpf.context = function (path) {
    if (undefined === path) {
        return _gpfMainContext;
    }
    return _gpfContext(path.split("."));
};

/*#ifndef(UMD)*/

/*exported _gpfFinishLoading*/ // Will be used by the boot specifics

var
    // Set to true once all sources of GPF are loaded
    _gpfLoaded = false,

    /**
     * List of functions to call on load
     *
     * @type {Function[]}
     */
    _gpfLoadCallbacks = [],

    // Callback used once all sources are loaded, invoke the callbacks
    _gpfFinishLoading = function () {
        _gpfLoaded = true;
        while (_gpfLoadCallbacks.length) {
            _gpfLoadCallbacks.shift()();
        }
    };

/**
 * Test if GPF is loaded
 *
 * @param {Function} [callback=undefined] callback This callback is invoked when GPF is loaded (may be immediately if
 * already loaded)
 * @return {Boolean} True if GPF is fully loaded, false otherwise
 */
gpf.loaded = function (callback) {
    if (callback) {
        if (_gpfLoaded) {
            callback();
        } else {
            _gpfLoadCallbacks.push(callback);
        }
    }
    return _gpfLoaded;
};

if(!gpf.loaded) {

/*#else*/

    gpf.loaded = function (callback) {
        if (callback) {
            callback();
        }
        return true;
    };

/*#endif*/

/*#ifndef(UMD)*/

}

/*#endif*/

/*#ifdef(DEBUG)*/

// DEBUG specifics

/**
 * Assertion helper
 *
 * @param {Boolean} condition May be a truthy value
 * @param {String} message Assertion message (to explain the violation if it fails)
 */
gpf.ASSERT = function (condition, message) {
    if (undefined === message) {
        message = "gpf.ASSERT with no message";
        condition = false;
    }
    if (!condition) {
        console.warn("ASSERTION FAILED: " + message);
        throw gpf.Error.AssertionFailed({
            message: message
        });
    }
};

/**
 * Batch assertion helper
 *
 * @param {Object} messages Dictionary of messages (value being the condition)
 */
gpf.ASSERTS = function (messages) {
    for (var message in messages) {
        if (messages.hasOwnProperty(message)) {
            gpf.ASSERT(messages[message], message);
        }
    }
};

if (!gpf.ASSERT) {

/*#else*/

    /*gpf:nop*/ gpf.ASSERT = _gpfEmptyFunc;
    /*gpf:nop*/ gpf.ASSERTS = _gpfEmptyFunc;

/*#endif*/

/*#ifdef(DEBUG)*/

}

/*#endif*/

/*#ifndef(UMD)*/

/**
 * Loading sources occurs here for the non UMD version.
 * UMD versions (debug / release) will have everything concatenated.
 */

// gpfSourcesPath - if defined - gives the relative path to sources
if ("undefined" === typeof gpfSourcesPath) {
    _gpfMainContext.gpfSourcesPath = "";
} else {
    var pathSep;
    if (_gpfDosPath) {
        pathSep = "\\";
    } else {
        pathSep = "/";
    }
    if (gpfSourcesPath.charAt(gpfSourcesPath.length - 1) !== pathSep) {
        gpfSourcesPath = gpfSourcesPath + pathSep;
    }
}

if (_GPF_HOST_WSCRIPT === _gpfHost) {
    _gpfMsFSO = new ActiveXObject("Scripting.FileSystemObject");
    (function () {
        var srcFile,
            srcContent;
        srcFile = _gpfMsFSO.OpenTextFile(gpfSourcesPath + "boot_ms.js");
        srcContent = srcFile.ReadAll();
        srcFile.Close();
        /*jslint evil: true*/
        eval(srcContent);
        /*jslint evil: false*/
    }());

} else if (_gpfInNode) {
    _gpfNodeFs =  require("fs");

    /**
     * Phantom/Node File System read text file method (boot)
     * @type {Function}
     */
    var _gpfFSRead;
    if (_GPF_HOST_PHANTOMJS === _gpfHost) {
        _gpfFSRead = _gpfNodeFs.read;
    } else {
        _gpfFSRead = function (path) {
            return _gpfNodeFs.readFileSync(path).toString();
        };
    }
    /*jslint evil: true*/
    eval(_gpfFSRead(gpfSourcesPath + "boot_node.js"));
    /*jslint evil: false*/

} else { // _GPF_HOST_BROWSER === _gpfHost
    var _gpfWebRawInclude = function (src) {
        var script = _gpfWebDocument.createElement("script");
        script.language = "javascript";
        script.src = gpfSourcesPath + src;
        _gpfWebHead.insertBefore(script, _gpfWebHead.firstChild);
    };
    _gpfWebRawInclude("boot_web.js");

}

/*#endif*/
<|endoftext|>