<commit_before>var Utils             = require("./utils")
  , DAOFactory        = require("./dao-factory")
  , DataTypes         = require('./data-types')
  , DAOFactoryManager = require("./dao-factory-manager")
  , Migrator          = require("./migrator")
  , QueryInterface    = require("./query-interface")

if(parseFloat(process.version.replace('v', '')) < 0.6) {
  console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.")
}

module.exports = (function() {
  /**
    Main constructor of the project.

    Params:

      - `database`
      - `username`
      - `password`, optional, default: null
      - `options`, optional, default: {}

    Examples:

        mymodule.write('foo')
        mymodule.write('foo', { stream: process.stderr })

  */
  var Sequelize = function(database, username, password, options) {
    this.options = Utils._.extend({
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      protocol: 'tcp',
      define: {},
      query: {},
      sync: {},
      logging: console.log,
      omitNull: false
    }, options || {})

    if(this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    this.config = {
      database: database,
      username: username,
      password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
      host    : this.options.host,
      port    : this.options.port,
      pool    : this.options.pool,
      protocol: this.options.protocol
    }

    var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")

    this.daoFactoryManager = new DAOFactoryManager(this)
    this.connectorManager  = new ConnectorManager(this, this.config)

    this.importCache = {}
  }

  /**
    Reference to Utils
  */
  Sequelize.Utils = Utils

  for (var dataType in DataTypes) {
    Sequelize[dataType] = DataTypes[dataType]
  }

  Sequelize.prototype.getQueryInterface = function() {
    this.queryInterface = this.queryInterface || new QueryInterface(this)
    return this.queryInterface
  }

  Sequelize.prototype.getMigrator = function(options, force) {
    if(force) {
      this.migrator = new Migrator(this, options)
    } else {
      this.migrator = this.migrator || new Migrator(this, options)
    }

    return this.migrator
  }

  Sequelize.prototype.define = function(daoName, attributes, options) {
    options = options || {}
    if(this.options.define) {
      options = Sequelize.Utils.merge(options, this.options.define)
    }
    options.omitNull = this.options.omitNull

    var factory = new DAOFactory(daoName, attributes, options)
    this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
    return factory
  }

  Sequelize.prototype.isDefined = function(daoName) {
    var daos = this.daoFactoryManager.daos
    return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
  }

  Sequelize.prototype.import = function(path) {
    if (!this.importCache[path]) {
      var defineCall = require(path)
      this.importCache[path] = defineCall(this, DataTypes)
    }

    return this.importCache[path]
  }

  Sequelize.prototype.migrate = function(options) {
    this.getMigrator().migrate(options)
  }

  Sequelize.prototype.query = function(sql, callee, options) {
    options = (arguments.length === 3) ? options : { raw: true }
    options = Utils._.extend(Utils._.clone(this.options.query), options)
    options = Utils._.extend(options, {
      logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log
    })

    return this.connectorManager.query(sql, callee, options)
  }

  Sequelize.prototype.sync = function(options) {
    options = options || {}

    if(this.options.sync) {
      options = Sequelize.Utils.merge(options, this.options.sync)
    }

    var chainer = new Utils.QueryChainer()

    this.daoFactoryManager.daos.forEach(function(dao) {
      chainer.add(dao.sync(options))
    })

    return chainer.run()
  }

  Sequelize.prototype.drop = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer

      self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })

      chainer
        .run()
        .success(function() { emitter.emit('success', null) })
        .error(function(err) { emitter.emit('error', err) })
    }).run()
  }

  return Sequelize
})()
<commit_msg>honor maxConcurrentQueries configuration option<commit_after>var Utils             = require("./utils")
  , DAOFactory        = require("./dao-factory")
  , DataTypes         = require('./data-types')
  , DAOFactoryManager = require("./dao-factory-manager")
  , Migrator          = require("./migrator")
  , QueryInterface    = require("./query-interface")

if(parseFloat(process.version.replace('v', '')) < 0.6) {
  console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.")
}

module.exports = (function() {
  /**
    Main constructor of the project.

    Params:

      - `database`
      - `username`
      - `password`, optional, default: null
      - `options`, optional, default: {}

    Examples:

        mymodule.write('foo')
        mymodule.write('foo', { stream: process.stderr })

  */
  var Sequelize = function(database, username, password, options) {
    this.options = Utils._.extend({
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      protocol: 'tcp',
      define: {},
      query: {},
      sync: {},
      logging: console.log,
      omitNull: false
    }, options || {})

    if(this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    this.config = {
      database: database,
      username: username,
      password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
      host    : this.options.host,
      port    : this.options.port,
      pool    : this.options.pool,
      protocol: this.options.protocol,
      maxConcurrentQueries: this.options.maxConcurrentQueries
    }

    var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")

    this.daoFactoryManager = new DAOFactoryManager(this)
    this.connectorManager  = new ConnectorManager(this, this.config)

    this.importCache = {}
  }

  /**
    Reference to Utils
  */
  Sequelize.Utils = Utils

  for (var dataType in DataTypes) {
    Sequelize[dataType] = DataTypes[dataType]
  }

  Sequelize.prototype.getQueryInterface = function() {
    this.queryInterface = this.queryInterface || new QueryInterface(this)
    return this.queryInterface
  }

  Sequelize.prototype.getMigrator = function(options, force) {
    if(force) {
      this.migrator = new Migrator(this, options)
    } else {
      this.migrator = this.migrator || new Migrator(this, options)
    }

    return this.migrator
  }

  Sequelize.prototype.define = function(daoName, attributes, options) {
    options = options || {}
    if(this.options.define) {
      options = Sequelize.Utils.merge(options, this.options.define)
    }
    options.omitNull = this.options.omitNull

    var factory = new DAOFactory(daoName, attributes, options)
    this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
    return factory
  }

  Sequelize.prototype.isDefined = function(daoName) {
    var daos = this.daoFactoryManager.daos
    return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
  }

  Sequelize.prototype.import = function(path) {
    if (!this.importCache[path]) {
      var defineCall = require(path)
      this.importCache[path] = defineCall(this, DataTypes)
    }

    return this.importCache[path]
  }

  Sequelize.prototype.migrate = function(options) {
    this.getMigrator().migrate(options)
  }

  Sequelize.prototype.query = function(sql, callee, options) {
    options = (arguments.length === 3) ? options : { raw: true }
    options = Utils._.extend(Utils._.clone(this.options.query), options)
    options = Utils._.extend(options, {
      logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log
    })

    return this.connectorManager.query(sql, callee, options)
  }

  Sequelize.prototype.sync = function(options) {
    options = options || {}

    if(this.options.sync) {
      options = Sequelize.Utils.merge(options, this.options.sync)
    }

    var chainer = new Utils.QueryChainer()

    this.daoFactoryManager.daos.forEach(function(dao) {
      chainer.add(dao.sync(options))
    })

    return chainer.run()
  }

  Sequelize.prototype.drop = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer

      self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })

      chainer
        .run()
        .success(function() { emitter.emit('success', null) })
        .error(function(err) { emitter.emit('error', err) })
    }).run()
  }

  return Sequelize
})()
<|endoftext|>


<commit_before>var webdriverio = require('webdriverio'),
    client = webdriverio.remote({
        desiredCapabilities: {
            browserName: 'chrome',
            version: '56',
            platform: 'WIN7',
            name: 'Compodoc test',
            'public': true
        },
        host: 'ondemand.saucelabs.com',
        port: 80,
        user: process.env.SAUCE_USERNAME,
        key: process.env.SAUCE_ACCESS_KEY,
        logLevel: 'verbose'
    }).init();

client
    .url('http://127.0.0.1:8383')
    .getTitle(function(err, title) {
        console.log(title);
    })
    .end();

/*
client = webdriverio.remote({
    desiredCapabilities: {
        browserName: 'firefox',
        version: '50',
        platform: 'XP',
        name: 'Compodoc test',
        'public': true
    },
    host: 'ondemand.saucelabs.com',
    port: 80,
    user: process.env.SAUCE_USERNAME,
    key: process.env.SAUCE_ACCESS_KEY,
    logLevel: 'verbose'
}).init()

client
    .url('http://google.com')
    .setValue('*[name="q"]', 'webdriverio')
    .click('*[name="btnG"]')
    .pause(1000)
    .getTitle(function(err, title) {
        console.log(title);
    })
    .end();
*/
<commit_msg>fix(test)<commit_after>var webdriverio = require('webdriverio'),
    client = webdriverio.remote({
        desiredCapabilities: {
            browserName: 'chrome',
            version: '56',
            platform: 'WIN7',
            'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
            name: 'Compodoc test',
            'public': true,
            build: process.env.TRAVIS_BUILD_NUMBER
        },
        host: 'ondemand.saucelabs.com',
        port: 80,
        user: process.env.SAUCE_USERNAME,
        key: process.env.SAUCE_ACCESS_KEY,
        logLevel: 'verbose'
    }).init();

client
    .url('http://127.0.0.1:8383')
    .getTitle(function(err, title) {
        console.log(title);
    })
    .end();

/*
client = webdriverio.remote({
    desiredCapabilities: {
        browserName: 'firefox',
        version: '50',
        platform: 'XP',
        name: 'Compodoc test',
        'public': true
    },
    host: 'ondemand.saucelabs.com',
    port: 80,
    user: process.env.SAUCE_USERNAME,
    key: process.env.SAUCE_ACCESS_KEY,
    logLevel: 'verbose'
}).init()

client
    .url('http://google.com')
    .setValue('*[name="q"]', 'webdriverio')
    .click('*[name="btnG"]')
    .pause(1000)
    .getTitle(function(err, title) {
        console.log(title);
    })
    .end();
*/
<|endoftext|>


<commit_before>import S from 'sanctuary';
import $ from 'sanctuary-def';
import Z from 'sanctuary-type-classes';
import {assert} from 'chai';
import {resolve} from '../src/signature';

// assertSameType :: Type -> Type -> Undefined !
const assertSameType = actual => expected => {
  return assert.isOk (S.equals (actual) (expected)) // FIXME directly replace assertTypePairs( S.zip (types) (..) ) by S.equals (types) (expecteds)
/*
  assert.strictEqual (actual.name, expected.name);
  assert.strictEqual (actual.type, expected.type);
  assert.deepEqual (actual.keys, expected.keys);
  assert.deepEqual (actual.url, expected.url);
  expected.keys.forEach (key => {
    assertSameType (actual.types[key].type) (expected.types[key].type);
  });
*/
};

const assertTypePairs = xs => xs.forEach
  (pair => assertSameType
      (S.fst (pair))
      (S.snd (pair)));

describe ('Function name', () => {
  it ('should be extracted', () => {
    const {name} = resolve ($) ([]) ($.env) ('foo :: Number -> Number');
    assert.strictEqual (name, 'foo');
  });
});

describe ('Type constraints', () => {
  it ('should return {} if not specified', () => {
    const {constraints} = resolve ($) ([]) ($.env) ('foo :: Number -> Number');
    assert.deepEqual (constraints, {});
  });

  it ('should resolve single constraint', () => {
    const tcs = [Z.Monoid];
    const {constraints} = resolve ($) (tcs) ($.env) ('foo :: Monoid a => a -> a');
    assert.deepEqual (constraints, {a: [Z.Monoid]});
  });

  it ('should resolve multiple contraints', () => {
    const tcs = [Z.Monoid, Z.Setoid];
    const {constraints} = resolve ($) (tcs) ($.env) ('foo :: (Monoid a, Setoid b) => a -> b');
    assert.deepEqual (constraints, {a: [Z.Monoid], b: [Z.Setoid]});
  });

  it ('should resolve multiple contraints on same variable', () => {
    const tcs = [Z.Monoid, Z.Setoid];
    const {constraints} = resolve ($) (tcs) ($.env) ('foo :: (Monoid a, Setoid a) => a -> b');
    assert.deepEqual (constraints, {a: [Z.Monoid, Z.Setoid]});
  });
});

describe ('Parameter types', () => {
  it ('should resolve built-in types', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> String');
    assert.deepEqual (types, [$.Number, $.String]);
  });

  it ('should resolve user types', () => {
    const Widget = $.NullaryType ('Widget') ('http://example.com/Widget') ([]) (S.K (true));
    const env = $.env.concat ([Widget]);
    const {types} = resolve ($) ([]) (env) ('foo :: Widget -> String');
    assert.deepEqual (types, [Widget, $.String]);
  });

  it ('should resolve namespaced user types', () => {
    const Widget = $.NullaryType ('x/y/z/Widget') ('http://example.com/Widget') ([]) (S.K (true));
    const env = $.env.concat ([Widget]);
    const {types} = resolve ($) ([]) (env) ('foo :: Widget -> String');
    assert.deepEqual (types, [Widget, $.String]);
  });

  it ('should resolve lists', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: [Number] -> [String]');
    const expecteds = [$.Array ($.Number), $.Array ($.String)];

    const lists = S.zip (types) (expecteds);
    assertTypePairs (lists);
  });

  it ('should resolve functions', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> (Number -> Number)');
    const lists = S.zip (types) ([$.Number, $.Function ([$.Number, $.Number])]);
    assertTypePairs (lists);
  });

  it ('should resolve functions that return multi-arity functions', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> (Number -> Number -> Number)');
    const lists = S.zip (types) ([$.Number, $.Function ([$.Number, $.Function ([$.Number, $.Number])])]);
    assertTypePairs (lists);
  });

  it ('should resolve higher-order functions that take multi-arity functions', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: (Number -> Number -> Number) -> Number');
    const lists = S.zip (types) ([
      $.Function ([
        $.Number,
        $.Function ([
          $.Number,
          $.Number,
        ]),
      ]),
      $.Number,
    ]);
    assertTypePairs (lists);
  });

  it ('should resolve unary types', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> StrMap Number');
    const lists = S.zip (types) ([$.Number, $.StrMap ($.Number)]);
    assertTypePairs (lists);
  });

  it ('should bark on wrong number of arguments', () => {
    const define = () => resolve ($) ([]) ($.env) ('foo :: Number -> StrMap Number Number');
    assert.throws (define, 'expects one argument, got two');
  });

  it ('should resolve typevars', () => {
    const a = $.TypeVariable ('a');
    const b = $.TypeVariable ('b');
    const {types} = resolve ($) ([]) ($.env) ('foo :: a -> b -> a');
    S.zip (types) ([a, b, a]).forEach (pair => {
      const actual = S.fst (pair);
      const expected = S.snd (pair);
      assert.equal (actual.name, expected.name);
      assert.equal (actual.type, expected.type);
      assert.deepEqual (actual.keys, expected.keys);
      assert.deepEqual (actual.types, expected.types);
    });
  });

  it ('should resolve maybes', () => {
    const Maybe = $.UnaryType
      ('my-package/Maybe')
      ('http://example.com/my-package#Maybe')
      ([])
      (S.K (true))
      (S.K ([]));

    const env = $.env.concat ([
      Maybe ($.Unknown),
    ]);
    const {types} = resolve ($) ([]) (env) ('foo :: Maybe String -> String');
    assertTypePairs (S.zip (types) ([Maybe ($.String), $.String]));
  });

  it ('should resolve eithers', () => {
    const Either = $.BinaryType
      ('my-package/Either')
      ('http://example.com/my-package#Either')
      ([])
      (x => x != null && x['@@type'] === 'my-package/Either')
      (either => (either.isLeft ? [either.value] : []))
      (either => (either.isRight ? [either.value] : []));

    const env = $.env.concat ([
      Either ($.Unknown) ($.Unknown),
    ]);
    const {types} = resolve ($) ([]) (env) ('foo :: Either String Number -> String');
    assertTypePairs (S.zip (types) ([Either ($.String) ($.Number), $.String]));
  });

  it ('should resolve thunks', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: () -> Number');
    assertTypePairs (S.zip (types) ([$.Number]));
  });

  it ('should resolve records', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: { value :: Number } -> Number');
    assertTypePairs (S.zip (types) ([$.RecordType ({value: $.Number}), $.Number]));
  });

  it ('should resolve constraints', () => {
    const a = $.TypeVariable ('a');
    const b = $.TypeVariable ('b');
    const f = $.UnaryTypeVariable ('f');
    const tcs = [Z.Functor];
    const {types} = resolve ($) (tcs) ($.env) ('foo :: Functor f => (a -> b) -> f a -> f b');
    assertTypePairs (S.zip (types) ([$.Function ([a, b]), f (a), f (b)]));
  });
});
<commit_msg>output diff of actual/expected in case of assert failure<commit_after>import S from 'sanctuary';
import $ from 'sanctuary-def';
import Z from 'sanctuary-type-classes';
import {assert} from 'chai';
import {resolve} from '../src/signature';
import show from 'sanctuary-show';

// assertSameType :: Type -> Type -> Undefined !
const assertSameType = actual => expected => {
  if (!(S.equals (actual) (expected))) { // Type is Setoid providing equals()
    assert.equal (show (actual), show (expected), "S.equals"); // assert.equal() because assert.isOk() does not diff actual/expected
    assert.fail(); // final failure in the hypothetical case that show() would return same strings for different types
  }
};

const assertTypePairs = xs => xs.forEach
  (pair => assertSameType
      (S.fst (pair))
      (S.snd (pair)));

describe ('Function name', () => {
  it ('should be extracted', () => {
    const {name} = resolve ($) ([]) ($.env) ('foo :: Number -> Number');
    assert.strictEqual (name, 'foo');
  });
});

describe ('Type constraints', () => {
  it ('should return {} if not specified', () => {
    const {constraints} = resolve ($) ([]) ($.env) ('foo :: Number -> Number');
    assert.deepEqual (constraints, {});
  });

  it ('should resolve single constraint', () => {
    const tcs = [Z.Monoid];
    const {constraints} = resolve ($) (tcs) ($.env) ('foo :: Monoid a => a -> a');
    assert.deepEqual (constraints, {a: [Z.Monoid]});
  });

  it ('should resolve multiple contraints', () => {
    const tcs = [Z.Monoid, Z.Setoid];
    const {constraints} = resolve ($) (tcs) ($.env) ('foo :: (Monoid a, Setoid b) => a -> b');
    assert.deepEqual (constraints, {a: [Z.Monoid], b: [Z.Setoid]});
  });

  it ('should resolve multiple contraints on same variable', () => {
    const tcs = [Z.Monoid, Z.Setoid];
    const {constraints} = resolve ($) (tcs) ($.env) ('foo :: (Monoid a, Setoid a) => a -> b');
    assert.deepEqual (constraints, {a: [Z.Monoid, Z.Setoid]});
  });
});

describe ('Parameter types', () => {
  it ('should resolve built-in types', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> String');
    assert.deepEqual (types, [$.Number, $.String]);
  });

  it ('should resolve user types', () => {
    const Widget = $.NullaryType ('Widget') ('http://example.com/Widget') ([]) (S.K (true));
    const env = $.env.concat ([Widget]);
    const {types} = resolve ($) ([]) (env) ('foo :: Widget -> String');
    assert.deepEqual (types, [Widget, $.String]);
  });

  it ('should resolve namespaced user types', () => {
    const Widget = $.NullaryType ('x/y/z/Widget') ('http://example.com/Widget') ([]) (S.K (true));
    const env = $.env.concat ([Widget]);
    const {types} = resolve ($) ([]) (env) ('foo :: Widget -> String');
    assert.deepEqual (types, [Widget, $.String]);
  });

  it ('should resolve lists', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: [Number] -> [String]');
    const expecteds = [$.Array ($.Number), $.Array ($.String)];

    const lists = S.zip (types) (expecteds);
    assertTypePairs (lists);
  });

  it ('should resolve functions', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> (Number -> Number)');
    const lists = S.zip (types) ([$.Number, $.Function ([$.Number, $.Number])]);
    assertTypePairs (lists);
  });

  it ('should resolve functions that return multi-arity functions', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> (Number -> Number -> Number)');
    const lists = S.zip (types) ([$.Number, $.Function ([$.Number, $.Function ([$.Number, $.Number])])]);
    assertTypePairs (lists);
  });

  it ('should resolve higher-order functions that take multi-arity functions', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: (Number -> Number -> Number) -> Number');
    const lists = S.zip (types) ([
      $.Function ([
        $.Number,
        $.Function ([
          $.Number,
          $.Number,
        ]),
      ]),
      $.Number,
    ]);
    assertTypePairs (lists);
  });

  it ('should resolve unary types', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: Number -> StrMap Number');
    const lists = S.zip (types) ([$.Number, $.StrMap ($.Number)]);
    assertTypePairs (lists);
  });

  it ('should bark on wrong number of arguments', () => {
    const define = () => resolve ($) ([]) ($.env) ('foo :: Number -> StrMap Number Number');
    assert.throws (define, 'expects one argument, got two');
  });

  it ('should resolve typevars', () => {
    const a = $.TypeVariable ('a');
    const b = $.TypeVariable ('b');
    const {types} = resolve ($) ([]) ($.env) ('foo :: a -> b -> a');
    S.zip (types) ([a, b, a]).forEach (pair => {
      const actual = S.fst (pair);
      const expected = S.snd (pair);
      assert.equal (actual.name, expected.name);
      assert.equal (actual.type, expected.type);
      assert.deepEqual (actual.keys, expected.keys);
      assert.deepEqual (actual.types, expected.types);
    });
  });

  it ('should resolve maybes', () => {
    const Maybe = $.UnaryType
      ('my-package/Maybe')
      ('http://example.com/my-package#Maybe')
      ([])
      (S.K (true))
      (S.K ([]));

    const env = $.env.concat ([
      Maybe ($.Unknown),
    ]);
    const {types} = resolve ($) ([]) (env) ('foo :: Maybe String -> String');
    assertTypePairs (S.zip (types) ([Maybe ($.String), $.String]));
  });

  it ('should resolve eithers', () => {
    const Either = $.BinaryType
      ('my-package/Either')
      ('http://example.com/my-package#Either')
      ([])
      (x => x != null && x['@@type'] === 'my-package/Either')
      (either => (either.isLeft ? [either.value] : []))
      (either => (either.isRight ? [either.value] : []));

    const env = $.env.concat ([
      Either ($.Unknown) ($.Unknown),
    ]);
    const {types} = resolve ($) ([]) (env) ('foo :: Either String Number -> String');
    assertTypePairs (S.zip (types) ([Either ($.String) ($.Number), $.String]));
  });

  it ('should resolve thunks', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: () -> Number');
    assertTypePairs (S.zip (types) ([$.Number]));
  });

  it ('should resolve records', () => {
    const {types} = resolve ($) ([]) ($.env) ('foo :: { value :: Number } -> Number');
    assertTypePairs (S.zip (types) ([$.RecordType ({value: $.Number}), $.Number]));
  });

  it ('should resolve constraints', () => {
    const a = $.TypeVariable ('a');
    const b = $.TypeVariable ('b');
    const f = $.UnaryTypeVariable ('f');
    const tcs = [Z.Functor];
    const {types} = resolve ($) (tcs) ($.env) ('foo :: Functor f => (a -> b) -> f a -> f b');
    assertTypePairs (S.zip (types) ([$.Function ([a, b]), f (a), f (b)]));
  });
});
<|endoftext|>


<commit_before>var vumigo = require("vumigo_v02");
var AppTester = vumigo.AppTester;
var assert = require("assert");
var fixtures_IdentityStore = require('./fixtures_identity_store');
var fixtures_StageBasedMessaging = require('./fixtures_stage_based_messaging');
var fixtures_MessageSender = require('./fixtures_message_sender');
var fixtures_Hub = require('./fixtures_hub');
var fixtures_Jembi = require('./fixtures_jembi');
var fixtures_Casepro = require('./fixtures_casepro');
var fixtures_ServiceRating = require('./fixtures_service_rating');

var SeedJsboxUtils = require("seed-jsbox-utils");
var utils = SeedJsboxUtils.utils;

describe("app", function() {
    describe("for sms_nurse use", function() {
        var app;
        var tester;

        beforeEach(function() {
            app = new go.app.GoNDOH();

            tester = new AppTester(app);

            tester
                .setup.char_limit(160)
                .setup.config.app({
                    name: "sms_nurse",
                    testing_today: "2014-04-04 07:07:07",
                    testing_message_id: "0170b7bb-978e-4b8a-35d2-662af5b6daee",
                    nurse_ussd_channel: "nurse_ussd_channel",
                    services: {
                        identity_store: {
                            url: "http://is/api/v1/",
                            token: "test IdentityStore"
                        },
                        hub: {
                            url: 'http://hub/api/v1/',
                            token: 'test Hub'
                        },
                        stage_based_messaging: {
                            url: "http://sbm/api/v1/",
                            token: "test StageBasedMessaging"
                        },
                        casepro: {
                            url: 'http://casepro/'
                        }
                    },
                    logging: "off",
                    env: 'test',
                    metric_store: 'test_metric_store',
                    public_holidays: [
                        "2015-01-01",  // new year's day
                        "2015-03-21",  // human rights day
                        "2015-04-03",  // good friday - VARIES
                        "2015-04-06",  // family day - VARIES
                        "2015-04-27",  // freedom day
                        "2015-05-01",  // worker's day
                        "2015-06-16",  // youth day
                        "2015-08-09",  // women's day
                        "2015-08-10",  // women's day OBSERVED (Sunday -> Monday)
                        "2015-09-24",  // heritage day
                        "2015-12-16",  // day of reconciliation
                        "2015-12-25",  // christmas day
                        "2015-12-26",  // day of goodwill
                    ],
                    helpdesk_hours: [8, 16]
                })
                .setup(function(api) {
                    api.metrics.stores = {'test_metric_store': {}};
                })
                .setup(function(api) {
                    // add fixtures for services used
                    fixtures_Hub().forEach(api.http.fixtures.add); // fixtures 0 - 49
                    fixtures_StageBasedMessaging().forEach(api.http.fixtures.add); // 50 - 99
                    fixtures_MessageSender().forEach(api.http.fixtures.add); // 100 - 149
                    fixtures_ServiceRating().forEach(api.http.fixtures.add); // 150 - 169
                    fixtures_Jembi().forEach(api.http.fixtures.add);  // 170 - 179
                    fixtures_IdentityStore().forEach(api.http.fixtures.add); // 180 ->
                    fixtures_Casepro().forEach(api.http.fixtures.add); // 243

                });
        });

        describe('using the session length helper', function () {
            it('should publish metrics', function () {
                return tester
                    .setup(function(api) {
                        api.kv.store['session_length_helper.' + api.config.app.name + '.foodacom.sentinel'] = '2000-12-12';
                        api.kv.store['session_length_helper.' + api.config.app.name + '.foodacom'] = 42;
                    })
                    .setup.user({
                        state: 'states_start',
                        addr: '27820001003',
                        metadata: {
                          session_length_helper: {
                            // one minute before the mocked timestamp
                            start: Number(new Date('April 4, 2014 07:06:07'))
                          }
                        }
                    })
                    .input({
                        content: 'start',
                        transport_metadata: {
                            aat_ussd: {
                                provider: 'foodacom'
                            }
                        }
                    })
                    .input.session_event('close')
                    .check(function(api, im) {

                        var kv_store = api.kv.store;
                        assert.equal(kv_store['session_length_helper.' + im.config.name + '.foodacom'], 60000);
                        assert.equal(
                          kv_store['session_length_helper.' + im.config.name + '.foodacom.sentinel'], '2014-04-04');

                        var m_store = api.metrics.stores.test_metric_store;
                        assert.equal(
                          m_store['session_length_helper.' + im.config.name + '.foodacom'].agg, 'max');
                        assert.equal(
                          m_store['session_length_helper.' + im.config.name + '.foodacom'].values[0], 60);
                    }).run();
            });
        });

        describe("test unique user Metrics", function() {

            describe("when a new unique user sends message in", function() {
                it("should increment the no. of unique users metric by 1", function() {
                    return tester
                        .setup.user.addr('27820001003')
                        .inputs(
                            'start'
                            )
                        .check(function(api) {
                            var metrics = api.metrics.stores.test_metric_store;
                            assert.deepEqual(metrics['test.sum.unique_users'].values, [1]);
                            assert.deepEqual(metrics['test.sum.unique_users.transient'].values, [1]);
                            assert.deepEqual(metrics['test.sms_nurse.sum.unique_users'].values, [1]);
                            assert.deepEqual(metrics['test.sms_nurse.sum.unique_users.transient'].values, [1]);
                        })
                        .run();
                });
            });
        });

        describe("when the user sends a message containing a USSD code", function() {
            it("should tell them to dial the number, not sms it", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .input("*134*12345# rate")
                    .check.interaction({
                        state: "states_dial_not_sms",
                        reply:
                            "Please use your handset's keypad to dial the number that you " +
                            "received, rather than sending it to us in an sms."
                    })
                    .run();
            });
        });

        describe("when the user sends a STOP message", function() {
            it("should set their opt out status", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .input("'stop' in the name of love")
                    .check.interaction({
                        state: "states_opt_out",
                        reply:
                            "Thank you. You will no longer receive messages from us."
                    })
                    .check(function(api) {
                        utils.check_fixtures_used(api, [35, 52, 54, 182, 192]);
                    })
                    .run();
            });
        });

        describe("when the user sends a BLOCK message", function() {
            it("should set their opt out status", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .input("BLOCK")
                    .check.interaction({
                        state: "states_opt_out",
                        reply:
                            "Thank you. You will no longer receive messages from us."
                    })
                    .check(function(api) {
                        utils.check_fixtures_used(api, [35, 52, 54, 182, 192]);
                    })
                    .run();
            });
        });

        describe("when the user sends a START message", function() {
            it("should reverse their opt out status", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .inputs("START")
                    .check.interaction({
                        state: "states_opt_in",
                        reply:
                            "Thank you. You will now receive messages from us again. " +
                            "If you have any medical concerns please visit your nearest clinic"
                    })
                    .check(function(api) {
                        utils.check_fixtures_used(api, [52, 54, 182, 240]);
                    })
                    .run();
            });
        });

        describe("when the user sends a different message", function() {
            describe("when the message is received between 08:00 and 16:00", function() {
                it("should log a support ticket", function() {
                    return tester
                        .setup.config.app({
                            // friday during working hours
                            testing_today: '2014-04-04 09:07:07'  // GMT+0200 (SAST)
                        })
                        .setup.user.addr('27820001003')
                        .inputs('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                'Thank you for your message, it has been captured and you will ' +
                                'receive a response soon. Kind regards. NurseConnect.'
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 243]);
                        })
                        .run();
                });
            });

            describe("when the message is received out of hours", function() {
                it("should give out of hours warning", function() {
                    return tester
                        .setup.config.app({
                            // friday out of hours
                            testing_today: '2014-04-04 04:07:07'
                        })
                        .setup.user.addr('27820001003')
                        .input('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                "The helpdesk operates from 8am to 4pm Mon to Fri. " +
                                "Responses will be delayed outside of these hrs."
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 243]);
                        })
                        .run();
                });
            });

            describe("when the message is received on a weekend", function() {
                it("should give weekend warning", function() {
                    return tester
                        .setup.config.app({
                            // saturday during working hours
                            testing_today: '2014-04-05 09:07:07'
                        })
                        .setup.user.addr('27820001003')
                        .input('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                "The helpdesk is not currently available during weekends " +
                                "and public holidays. Responses will be delayed during this time."
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 243]);
                        })
                        .run();
                });
            });

            describe("when the message is received on a public holiday", function() {
                it("should give public holiday warning", function() {
                    return tester
                        .setup.config.app({
                            // women's day 2015 during working hours
                            testing_today: '2015-08-10 09:07:07'
                        })
                        .setup.user.addr('27820001003')
                        .input('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                "The helpdesk is not currently available during weekends " +
                                "and public holidays. Responses will be delayed during this time."
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 243]);
                        })
                        .run();
                });
            });
        });

    });
});
<commit_msg>Updated support for multiple messagesets<commit_after>var vumigo = require("vumigo_v02");
var AppTester = vumigo.AppTester;
var assert = require("assert");
var fixtures_IdentityStore = require('./fixtures_identity_store');
var fixtures_StageBasedMessaging = require('./fixtures_stage_based_messaging');
var fixtures_MessageSender = require('./fixtures_message_sender');
var fixtures_Hub = require('./fixtures_hub');
var fixtures_Jembi = require('./fixtures_jembi');
var fixtures_Casepro = require('./fixtures_casepro');
var fixtures_ServiceRating = require('./fixtures_service_rating');

var SeedJsboxUtils = require("seed-jsbox-utils");
var utils = SeedJsboxUtils.utils;

describe("app", function() {
    describe("for sms_nurse use", function() {
        var app;
        var tester;

        beforeEach(function() {
            app = new go.app.GoNDOH();

            tester = new AppTester(app);

            tester
                .setup.char_limit(160)
                .setup.config.app({
                    name: "sms_nurse",
                    testing_today: "2014-04-04 07:07:07",
                    testing_message_id: "0170b7bb-978e-4b8a-35d2-662af5b6daee",
                    nurse_ussd_channel: "nurse_ussd_channel",
                    services: {
                        identity_store: {
                            url: "http://is/api/v1/",
                            token: "test IdentityStore"
                        },
                        hub: {
                            url: 'http://hub/api/v1/',
                            token: 'test Hub'
                        },
                        stage_based_messaging: {
                            url: "http://sbm/api/v1/",
                            token: "test StageBasedMessaging"
                        },
                        casepro: {
                            url: 'http://casepro/'
                        }
                    },
                    logging: "off",
                    env: 'test',
                    metric_store: 'test_metric_store',
                    public_holidays: [
                        "2015-01-01",  // new year's day
                        "2015-03-21",  // human rights day
                        "2015-04-03",  // good friday - VARIES
                        "2015-04-06",  // family day - VARIES
                        "2015-04-27",  // freedom day
                        "2015-05-01",  // worker's day
                        "2015-06-16",  // youth day
                        "2015-08-09",  // women's day
                        "2015-08-10",  // women's day OBSERVED (Sunday -> Monday)
                        "2015-09-24",  // heritage day
                        "2015-12-16",  // day of reconciliation
                        "2015-12-25",  // christmas day
                        "2015-12-26",  // day of goodwill
                    ],
                    helpdesk_hours: [8, 16]
                })
                .setup(function(api) {
                    api.metrics.stores = {'test_metric_store': {}};
                })
                .setup(function(api) {
                    // add fixtures for services used
                    fixtures_Hub().forEach(api.http.fixtures.add); // fixtures 0 - 49
                    fixtures_StageBasedMessaging().forEach(api.http.fixtures.add); // 50 - 99
                    fixtures_MessageSender().forEach(api.http.fixtures.add); // 100 - 149
                    fixtures_ServiceRating().forEach(api.http.fixtures.add); // 150 - 169
                    fixtures_Jembi().forEach(api.http.fixtures.add);  // 170 - 179
                    fixtures_IdentityStore().forEach(api.http.fixtures.add); // 180 ->
                    fixtures_Casepro().forEach(api.http.fixtures.add); // 243

                });
        });

        describe('using the session length helper', function () {
            it('should publish metrics', function () {
                return tester
                    .setup(function(api) {
                        api.kv.store['session_length_helper.' + api.config.app.name + '.foodacom.sentinel'] = '2000-12-12';
                        api.kv.store['session_length_helper.' + api.config.app.name + '.foodacom'] = 42;
                    })
                    .setup.user({
                        state: 'states_start',
                        addr: '27820001003',
                        metadata: {
                          session_length_helper: {
                            // one minute before the mocked timestamp
                            start: Number(new Date('April 4, 2014 07:06:07'))
                          }
                        }
                    })
                    .input({
                        content: 'start',
                        transport_metadata: {
                            aat_ussd: {
                                provider: 'foodacom'
                            }
                        }
                    })
                    .input.session_event('close')
                    .check(function(api, im) {

                        var kv_store = api.kv.store;
                        assert.equal(kv_store['session_length_helper.' + im.config.name + '.foodacom'], 60000);
                        assert.equal(
                          kv_store['session_length_helper.' + im.config.name + '.foodacom.sentinel'], '2014-04-04');

                        var m_store = api.metrics.stores.test_metric_store;
                        assert.equal(
                          m_store['session_length_helper.' + im.config.name + '.foodacom'].agg, 'max');
                        assert.equal(
                          m_store['session_length_helper.' + im.config.name + '.foodacom'].values[0], 60);
                    }).run();
            });
        });

        describe("test unique user Metrics", function() {

            describe("when a new unique user sends message in", function() {
                it("should increment the no. of unique users metric by 1", function() {
                    return tester
                        .setup.user.addr('27820001003')
                        .inputs(
                            'start'
                            )
                        .check(function(api) {
                            var metrics = api.metrics.stores.test_metric_store;
                            assert.deepEqual(metrics['test.sum.unique_users'].values, [1]);
                            assert.deepEqual(metrics['test.sum.unique_users.transient'].values, [1]);
                            assert.deepEqual(metrics['test.sms_nurse.sum.unique_users'].values, [1]);
                            assert.deepEqual(metrics['test.sms_nurse.sum.unique_users.transient'].values, [1]);
                        })
                        .run();
                });
            });
        });

        describe("when the user sends a message containing a USSD code", function() {
            it("should tell them to dial the number, not sms it", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .input("*134*12345# rate")
                    .check.interaction({
                        state: "states_dial_not_sms",
                        reply:
                            "Please use your handset's keypad to dial the number that you " +
                            "received, rather than sending it to us in an sms."
                    })
                    .run();
            });
        });

        describe("when the user sends a STOP message", function() {
            it("should set their opt out status", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .input("'stop' in the name of love")
                    .check.interaction({
                        state: "states_opt_out",
                        reply:
                            "Thank you. You will no longer receive messages from us."
                    })
                    .check(function(api) {
                        utils.check_fixtures_used(api, [35, 52, 54, 182, 192]);
                    })
                    .run();
            });
        });

        describe("when the user sends a BLOCK message", function() {
            it("should set their opt out status", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .input("BLOCK")
                    .check.interaction({
                        state: "states_opt_out",
                        reply:
                            "Thank you. You will no longer receive messages from us."
                    })
                    .check(function(api) {
                        utils.check_fixtures_used(api, [35, 52, 54, 182, 192]);
                    })
                    .run();
            });
        });

        describe("when the user sends a START message", function() {
            it("should reverse their opt out status", function() {
                return tester
                    .setup.user.addr("27820001003")
                    .inputs("START")
                    .check.interaction({
                        state: "states_opt_in",
                        reply:
                            "Thank you. You will now receive messages from us again. " +
                            "If you have any medical concerns please visit your nearest clinic"
                    })
                    .check(function(api) {
                        utils.check_fixtures_used(api, [52, 54, 182, 240]);
                    })
                    .run();
            });
        });

        describe("when the user sends a different message", function() {
            describe("when the message is received between 08:00 and 16:00", function() {
                it("should log a support ticket", function() {
                    return tester
                        .setup.config.app({
                            // friday during working hours
                            testing_today: '2014-04-04 09:07:07'  // GMT+0200 (SAST)
                        })
                        .setup.user.addr('27820001003')
                        .inputs('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                'Thank you for your message, it has been captured and you will ' +
                                'receive a response soon. Kind regards. NurseConnect.'
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 244]);
                        })
                        .run();
                });
            });

            describe("when the message is received out of hours", function() {
                it("should give out of hours warning", function() {
                    return tester
                        .setup.config.app({
                            // friday out of hours
                            testing_today: '2014-04-04 04:07:07'
                        })
                        .setup.user.addr('27820001003')
                        .input('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                "The helpdesk operates from 8am to 4pm Mon to Fri. " +
                                "Responses will be delayed outside of these hrs."
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 244]);
                        })
                        .run();
                });
            });

            describe("when the message is received on a weekend", function() {
                it("should give weekend warning", function() {
                    return tester
                        .setup.config.app({
                            // saturday during working hours
                            testing_today: '2014-04-05 09:07:07'
                        })
                        .setup.user.addr('27820001003')
                        .input('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                "The helpdesk is not currently available during weekends " +
                                "and public holidays. Responses will be delayed during this time."
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 244]);
                        })
                        .run();
                });
            });

            describe("when the message is received on a public holiday", function() {
                it("should give public holiday warning", function() {
                    return tester
                        .setup.config.app({
                            // women's day 2015 during working hours
                            testing_today: '2015-08-10 09:07:07'
                        })
                        .setup.user.addr('27820001003')
                        .input('DONUTS')
                        .check.interaction({
                            state: 'states_default',
                            reply:
                                "The helpdesk is not currently available during weekends " +
                                "and public holidays. Responses will be delayed during this time."
                        })
                        .check(function(api) {
                            utils.check_fixtures_used(api, [52, 54, 182, 244]);
                        })
                        .run();
                });
            });
        });

    });
});
<|endoftext|>


<commit_before>var expect = require('chai').expect;
var sinon = require('sinon');
var Promise = require('bluebird');
var inquirer = require('inquirer');
var ReadlineStub = require('../helpers/readline');
var Prompt = require('../../index');

describe('inquirer-autocomplete-prompt', function() {
  var source;
  var prompt;
  var resolve;
  var reject;
  var promise;
  var rl;
  var defaultChoices;

  beforeEach(function() {
    defaultChoices = ['foo', new inquirer.Separator(), 'bar', 'bum'];
    promise = new Promise(function(res, rej) {
      resolve = res;
      reject = rej;
    });
    source = sinon.stub().returns(promise);

    rl = new ReadlineStub();
    prompt = new Prompt({
      message: 'test',
      name: 'name',
      source: source
    }, rl);
  });

  it('requires a name', function() {
    expect(function() {
      new Prompt({
        message: 'foo',
        source: source
      });

    }).to.throw(/name/);
  });

  it('requires a message', function() {
    expect(function() {
      new Prompt({
        name: 'foo',
        source: source
      });

    }).to.throw(/message/);
  });

  it('requires a source parameter', function() {
    expect(function() {
      new Prompt({
        name: 'foo',
        message: 'foo',
      });

    }).to.throw(/source/);
  });

  it('immediately calls source with null', function() {
    prompt.run();
    sinon.assert.calledOnce(source);
    sinon.assert.calledWithExactly(source, undefined, null);
  });

  describe('when it has some results', function() {
    var promiseForAnswer;
    beforeEach(function() {
      promiseForAnswer = getPromiseForAnswer();
      resolve(defaultChoices);
      return promise;
    });

    it('should move selected cursor on keypress', function() {
      moveDown();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('bar');
      })
    });

    it('moves up and down', function() {
      moveDown();
      moveDown();
      moveUp();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('bar');
      })
    });

    it('loops choices going down', function() {
      moveDown();
      moveDown();
      moveDown();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('foo');
      })
    });

    it('loops choices going up', function() {
      moveUp();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('bum');
      })
    })
  });

  describe('searching', function() {
    beforeEach(function() {
      prompt.run();
      source.reset();
    });

    it('searches after each char when user types', function() {
      type('a');
      sinon.assert.calledWithExactly(source, undefined, 'a');
      type('bba');
      sinon.assert.calledWithExactly(source, undefined, 'ab');
      sinon.assert.calledWithExactly(source, undefined, 'abb');
      sinon.assert.calledWithExactly(source, undefined, 'abba');
      sinon.assert.callCount(source, 4);
    });

    it('does not search again if same searchterm (not input added)', function() {
      type('ice');
      sinon.assert.calledThrice(source);
      source.reset();
      typeNonChar();
      sinon.assert.notCalled(source);
    });
  });

  describe('submit', function() {

    describe('without choices', function() {

      beforeEach(function() {
        prompt.run();
        source.reset();
      });

      it('searches again, since not possible to select something that does not exist', function() {
        sinon.assert.notCalled(source);
        enter();
        sinon.assert.calledOnce(source);
      });
    });

    describe('with choices', function() {
      var promiseForAnswer;

      beforeEach(function() {
        promiseForAnswer = getPromiseForAnswer();
        resolve(defaultChoices);
        return promise;
      });

      it('stores the value as the answer and status to answered', function() {
        enter();
        return promiseForAnswer.then(function(answer) {
          expect(answer).to.equal('foo');
          expect(prompt.answer).to.equal('foo');
          expect(prompt.status).to.equal('answered');
        })
      });

      describe('after selecting', function() {
        beforeEach(function() {
          enter();
          source.reset();
          return promiseForAnswer;
        });

        it('stops searching on typing', function() {
          type('test');
          sinon.assert.notCalled(source);
        });

        it('does not change answer on enter', function() {
          enter();
          sinon.assert.notCalled(source);
          return promiseForAnswer.then(function(answer) {
            expect(answer).to.equal('foo');
            expect(prompt.answer).to.equal('foo');
            expect(prompt.status).to.equal('answered');
          })
        });
      });
    });

  });

  function getPromiseForAnswer() {
    return prompt.run();
  }

  function typeNonChar() {
    rl.emit('keypress', '', {
      name: 'shift'
    });
  }

  function type(word) {
    word.split('').forEach(function(char) {
      rl.line = rl.line + char;
      rl.emit('keypress', char)
    });
  }

  function moveDown() {
    rl.emit('keypress', '', {
      name: 'down'
    });
  }

  function moveUp() {
    rl.emit('keypress', '', {
      name: 'up'
    });
  }

  function enter() {
    rl.emit('line');
  }

})
<commit_msg>Fix keypress events in tests.<commit_after>var expect = require('chai').expect;
var sinon = require('sinon');
var Promise = require('bluebird');
var inquirer = require('inquirer');
var ReadlineStub = require('../helpers/readline');
var Prompt = require('../../index');

describe('inquirer-autocomplete-prompt', function() {
  var source;
  var prompt;
  var resolve;
  var reject;
  var promise;
  var rl;
  var defaultChoices;

  beforeEach(function() {
    defaultChoices = ['foo', new inquirer.Separator(), 'bar', 'bum'];
    promise = new Promise(function(res, rej) {
      resolve = res;
      reject = rej;
    });
    source = sinon.stub().returns(promise);

    rl = new ReadlineStub();
    prompt = new Prompt({
      message: 'test',
      name: 'name',
      source: source
    }, rl);
  });

  it('requires a name', function() {
    expect(function() {
      new Prompt({
        message: 'foo',
        source: source
      });

    }).to.throw(/name/);
  });

  it('requires a message', function() {
    expect(function() {
      new Prompt({
        name: 'foo',
        source: source
      });

    }).to.throw(/message/);
  });

  it('requires a source parameter', function() {
    expect(function() {
      new Prompt({
        name: 'foo',
        message: 'foo',
      });

    }).to.throw(/source/);
  });

  it('immediately calls source with null', function() {
    prompt.run();
    sinon.assert.calledOnce(source);
    sinon.assert.calledWithExactly(source, undefined, null);
  });

  describe('when it has some results', function() {
    var promiseForAnswer;
    beforeEach(function() {
      promiseForAnswer = getPromiseForAnswer();
      resolve(defaultChoices);
      return promise;
    });

    it('should move selected cursor on keypress', function() {
      moveDown();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('bar');
      })
    });

    it('moves up and down', function() {
      moveDown();
      moveDown();
      moveUp();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('bar');
      })
    });

    it('loops choices going down', function() {
      moveDown();
      moveDown();
      moveDown();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('foo');
      })
    });

    it('loops choices going up', function() {
      moveUp();
      enter();

      return promiseForAnswer.then(function(answer) {
        expect(answer).to.equal('bum');
      })
    })
  });

  describe('searching', function() {
    beforeEach(function() {
      prompt.run();
      source.reset();
    });

    it('searches after each char when user types', function() {
      type('a');
      sinon.assert.calledWithExactly(source, undefined, 'a');
      type('bba');
      sinon.assert.calledWithExactly(source, undefined, 'ab');
      sinon.assert.calledWithExactly(source, undefined, 'abb');
      sinon.assert.calledWithExactly(source, undefined, 'abba');
      sinon.assert.callCount(source, 4);
    });

    it('does not search again if same searchterm (not input added)', function() {
      type('ice');
      sinon.assert.calledThrice(source);
      source.reset();
      typeNonChar();
      sinon.assert.notCalled(source);
    });
  });

  describe('submit', function() {

    describe('without choices', function() {

      beforeEach(function() {
        prompt.run();
        source.reset();
      });

      it('searches again, since not possible to select something that does not exist', function() {
        sinon.assert.notCalled(source);
        enter();
        sinon.assert.calledOnce(source);
      });
    });

    describe('with choices', function() {
      var promiseForAnswer;

      beforeEach(function() {
        promiseForAnswer = getPromiseForAnswer();
        resolve(defaultChoices);
        return promise;
      });

      it('stores the value as the answer and status to answered', function() {
        enter();
        return promiseForAnswer.then(function(answer) {
          expect(answer).to.equal('foo');
          expect(prompt.answer).to.equal('foo');
          expect(prompt.status).to.equal('answered');
        })
      });

      describe('after selecting', function() {
        beforeEach(function() {
          enter();
          source.reset();
          return promiseForAnswer;
        });

        it('stops searching on typing', function() {
          type('test');
          sinon.assert.notCalled(source);
        });

        it('does not change answer on enter', function() {
          enter();
          sinon.assert.notCalled(source);
          return promiseForAnswer.then(function(answer) {
            expect(answer).to.equal('foo');
            expect(prompt.answer).to.equal('foo');
            expect(prompt.status).to.equal('answered');
          })
        });
      });
    });

  });

  function getPromiseForAnswer() {
    return prompt.run();
  }

  function typeNonChar() {
    rl.input.emit('keypress', '', {
      name: 'shift'
    });
  }

  function type(word) {
    word.split('').forEach(function(char) {
      rl.line = rl.line + char;
      rl.input.emit('keypress', char)
    });
  }

  function moveDown() {
    rl.input.emit('keypress', '', {
      name: 'down'
    });
  }

  function moveUp() {
    rl.input.emit('keypress', '', {
      name: 'up'
    });
  }

  function enter() {
    rl.emit('line');
  }

})
<|endoftext|>


<commit_before>
var emptyCallback = function(cb) {
  cb && cb();
}

exports.sequence = function(actions) {
  var self = function() {
    actions.length && actions.shift()(self);
  }
  self();
}

exports.collect = function(actions, finalCallback) {
  var size    = actions.length;
  var finalcb = finalCallback || emptyCallback;

  for (var i=0; i<actions.length; i++) {
    actions[i](function() {
        // console.log("Size is " + size);
        if (--size == 0) {
          // console.log("Finished block");
          finalcb();
        }
      }
    );
  }
}

exports.pipeline = function(actions, finalCallback, maxOpenCalls) {

  var openCalls = 0;
  var finalcb   = finalCallback || emptyCallback;
  var maxOpen   = maxOpenCalls  || Infinity;

  function callback() {
    if (--openCalls < maxOpen) {
      launch();
    }
  }

  function launch() {
    if (actions.length != 0) {
      do {
        openCalls++;
        actions.shift()(callback);
      } while (openCalls < maxOpen);
    } else if ((actions.length == 0) && (openCalls == 0)){
      // console.log("Finished all calls");
      finalcb();
    } else {
      // console.log("Too many calls open, waiting");
    }
  }

  var initial = (maxOpen > actions.length) ? maxOpen : actions.length;

  for (var i=0; i<initial; i++) {
    launch();
  }
}

<commit_msg>Added an extra check to the pipeline/launch loop, put multiple condition checks on multiple lines<commit_after>
var emptyCallback = function(cb) {
  cb && cb();
}

exports.sequence = function(actions) {
  var self = function() {
    actions.length && actions.shift()(self);
  }
  self();
}

exports.collect = function(actions, finalCallback) {
  var size    = actions.length;
  var finalcb = finalCallback || emptyCallback;

  for (var i=0; i<actions.length; i++) {
    actions[i](function() {
        // console.log("Size is " + size);
        if (--size == 0) {
          // console.log("Finished block");
          finalcb();
        }
      }
    );
  }
}

exports.pipeline = function(actions, finalCallback, maxOpenCalls) {

  var openCalls = 0;
  var finalcb   = finalCallback || emptyCallback;
  var maxOpen   = maxOpenCalls  || Infinity;

  function callback() {
    if (--openCalls < maxOpen) {
      launch();
    }
  }

  function launch() {
    if (actions.length != 0) {
      do {
        openCalls++;
        actions.shift()(callback);
      } while (
        (openCalls < maxOpen) &&
        (actions.length != 0)
      );
    } else if (
      (actions.length == 0) &&
      (openCalls == 0)
    ) {
      // console.log("Finished all calls");
      finalcb();
    } else {
      // console.log("Too many calls open, waiting");
    }
  }

  var initial = (maxOpen > actions.length) ? maxOpen : actions.length;

  for (var i=0; i<initial; i++) {
    launch();
  }
}

<|endoftext|>


<commit_before>import {
  hasOwn
}
  from 'uni-shared'

export const pixelRatio = (function () {
  const canvas = document.createElement('canvas')
  canvas.height = canvas.width = 0
  const context = canvas.getContext('2d')
  const backingStore = context.backingStorePixelRatio ||
    context.webkitBackingStorePixelRatio ||
    context.mozBackingStorePixelRatio ||
    context.msBackingStorePixelRatio ||
    context.oBackingStorePixelRatio ||
    context.backingStorePixelRatio || 1
  return (window.devicePixelRatio || 1) / backingStore
})()

const forEach = function (obj, func) {
  for (const key in obj) {
    if (hasOwn(obj, key)) {
      func(obj[key], key)
    }
  }
}
const ratioArgs = {
  fillRect: 'all',
  clearRect: 'all',
  strokeRect: 'all',
  moveTo: 'all',
  lineTo: 'all',
  arc: [0, 1, 2],
  arcTo: 'all',
  bezierCurveTo: 'all',
  isPointinPath: 'all',
  isPointinStroke: 'all',
  quadraticCurveTo: 'all',
  rect: 'all',
  translate: 'all',
  createRadialGradient: 'all',
  createLinearGradient: 'all',
  setTransform: [4, 5]
}

const proto = CanvasRenderingContext2D.prototype

proto.drawImageByCanvas = (function (_super) {
  return function (canvas, srcx, srcy, srcw, srch, desx, desy, desw, desh, isScale) {
    if (!this.__hidpi__) {
      return _super.apply(this, arguments)
    }
    srcx *= pixelRatio
    srcy *= pixelRatio
    srcw *= pixelRatio
    srch *= pixelRatio
    desx *= pixelRatio
    desy *= pixelRatio
    desw = isScale ? desw * pixelRatio : desw
    desh = isScale ? desh * pixelRatio : desh
    _super.call(this, canvas, srcx, srcy, srcw, srch, desx, desy, desw, desh)
  }
})(proto.drawImage)

if (pixelRatio !== 1) {
  forEach(ratioArgs, function (value, key) {
    proto[key] = (function (_super) {
      return function () {
        if (!this.__hidpi__) {
          return _super.apply(this, arguments)
        }

        let args = Array.prototype.slice.call(arguments)

        if (value === 'all') {
          args = args.map(function (a) {
            return a * pixelRatio
          })
        } else if (Array.isArray(value)) {
          for (let i = 0; i < value.length; i++) {
            args[value[i]] *= pixelRatio
          }
        }
        return _super.apply(this, args)
      }
    })(proto[key])
  })

  proto.stroke = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      this.lineWidth *= pixelRatio
      _super.apply(this, arguments)
      this.lineWidth /= pixelRatio
    }
  })(proto.stroke)

  proto.fillText = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      const args = Array.prototype.slice.call(arguments)

      args[1] *= pixelRatio
      args[2] *= pixelRatio
      args[3] *= pixelRatio

      // Safari 重新设置部分属性会导致其他值恢复默认，需获取原始值
      var font = this.__font__ || this.font
      this.font = font.replace(
        /(\d+\.?\d*)(px|em|rem|pt)/g,
        function (w, m, u) {
          return (m * pixelRatio) + u
        }
      )

      _super.apply(this, args)

      this.font = font
    }
  })(proto.fillText)

  proto.strokeText = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      var args = Array.prototype.slice.call(arguments)

      args[1] *= pixelRatio // x
      args[2] *= pixelRatio // y
      args[3] *= pixelRatio // y

      // Safari 重新设置部分属性会导致其他值恢复默认，需获取原始值
      var font = this.__font__ || this.font
      this.font = font.replace(
        /(\d+\.?\d*)(px|em|rem|pt)/g,
        function (w, m, u) {
          return (m * pixelRatio) + u
        }
      )
      _super.apply(this, args)

      this.font = font
    }
  })(proto.strokeText)

  proto.drawImage = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      this.scale(pixelRatio, pixelRatio)
      _super.apply(this, arguments)
      this.scale(1 / pixelRatio, 1 / pixelRatio)
    }
  })(proto.drawImage)
}

export function wrapper (canvas, hidpi = true) {
  canvas.width = canvas.offsetWidth * (hidpi ? pixelRatio : 1)
  canvas.height = canvas.offsetHeight * (hidpi ? pixelRatio : 1)
  canvas.__hidpi__ = hidpi
  // 避免低版本安卓上 context 实例被回收
  canvas.__context2d__ = canvas.getContext('2d')
  canvas.__context2d__.__hidpi__ = hidpi
}
<commit_msg>fix: __hidpi__ fillText、strokeText maxWidth<commit_after>import {
  hasOwn
}
  from 'uni-shared'

export const pixelRatio = (function () {
  const canvas = document.createElement('canvas')
  canvas.height = canvas.width = 0
  const context = canvas.getContext('2d')
  const backingStore = context.backingStorePixelRatio ||
    context.webkitBackingStorePixelRatio ||
    context.mozBackingStorePixelRatio ||
    context.msBackingStorePixelRatio ||
    context.oBackingStorePixelRatio ||
    context.backingStorePixelRatio || 1
  return (window.devicePixelRatio || 1) / backingStore
})()

const forEach = function (obj, func) {
  for (const key in obj) {
    if (hasOwn(obj, key)) {
      func(obj[key], key)
    }
  }
}
const ratioArgs = {
  fillRect: 'all',
  clearRect: 'all',
  strokeRect: 'all',
  moveTo: 'all',
  lineTo: 'all',
  arc: [0, 1, 2],
  arcTo: 'all',
  bezierCurveTo: 'all',
  isPointinPath: 'all',
  isPointinStroke: 'all',
  quadraticCurveTo: 'all',
  rect: 'all',
  translate: 'all',
  createRadialGradient: 'all',
  createLinearGradient: 'all',
  setTransform: [4, 5]
}

const proto = CanvasRenderingContext2D.prototype

proto.drawImageByCanvas = (function (_super) {
  return function (canvas, srcx, srcy, srcw, srch, desx, desy, desw, desh, isScale) {
    if (!this.__hidpi__) {
      return _super.apply(this, arguments)
    }
    srcx *= pixelRatio
    srcy *= pixelRatio
    srcw *= pixelRatio
    srch *= pixelRatio
    desx *= pixelRatio
    desy *= pixelRatio
    desw = isScale ? desw * pixelRatio : desw
    desh = isScale ? desh * pixelRatio : desh
    _super.call(this, canvas, srcx, srcy, srcw, srch, desx, desy, desw, desh)
  }
})(proto.drawImage)

if (pixelRatio !== 1) {
  forEach(ratioArgs, function (value, key) {
    proto[key] = (function (_super) {
      return function () {
        if (!this.__hidpi__) {
          return _super.apply(this, arguments)
        }

        let args = Array.prototype.slice.call(arguments)

        if (value === 'all') {
          args = args.map(function (a) {
            return a * pixelRatio
          })
        } else if (Array.isArray(value)) {
          for (let i = 0; i < value.length; i++) {
            args[value[i]] *= pixelRatio
          }
        }
        return _super.apply(this, args)
      }
    })(proto[key])
  })

  proto.stroke = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      this.lineWidth *= pixelRatio
      _super.apply(this, arguments)
      this.lineWidth /= pixelRatio
    }
  })(proto.stroke)

  proto.fillText = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      const args = Array.prototype.slice.call(arguments)

      args[1] *= pixelRatio
      args[2] *= pixelRatio
      args[3] *= pixelRatio
      typeof args[3] !== 'number' && (args.length = 3)

      // Safari 重新设置部分属性会导致其他值恢复默认，需获取原始值
      var font = this.__font__ || this.font
      this.font = font.replace(
        /(\d+\.?\d*)(px|em|rem|pt)/g,
        function (w, m, u) {
          return (m * pixelRatio) + u
        }
      )

      _super.apply(this, args)

      this.font = font
    }
  })(proto.fillText)

  proto.strokeText = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      var args = Array.prototype.slice.call(arguments)

      args[1] *= pixelRatio // x
      args[2] *= pixelRatio // y
      args[3] *= pixelRatio // y
      typeof args[3] !== 'number' && (args.length = 3)

      // Safari 重新设置部分属性会导致其他值恢复默认，需获取原始值
      var font = this.__font__ || this.font
      this.font = font.replace(
        /(\d+\.?\d*)(px|em|rem|pt)/g,
        function (w, m, u) {
          return (m * pixelRatio) + u
        }
      )
      _super.apply(this, args)

      this.font = font
    }
  })(proto.strokeText)

  proto.drawImage = (function (_super) {
    return function () {
      if (!this.__hidpi__) {
        return _super.apply(this, arguments)
      }
      this.scale(pixelRatio, pixelRatio)
      _super.apply(this, arguments)
      this.scale(1 / pixelRatio, 1 / pixelRatio)
    }
  })(proto.drawImage)
}

export function wrapper (canvas, hidpi = true) {
  canvas.width = canvas.offsetWidth * (hidpi ? pixelRatio : 1)
  canvas.height = canvas.offsetHeight * (hidpi ? pixelRatio : 1)
  canvas.__hidpi__ = hidpi
  // 避免低版本安卓上 context 实例被回收
  canvas.__context2d__ = canvas.getContext('2d')
  canvas.__context2d__.__hidpi__ = hidpi
}
<|endoftext|>


<commit_before>/**
# RENDERER: dom
*/
reg('renderer', 'dom', function(view, panFrame, container, params, baseRenderer) {

    /* internals */

    var ID_PREFIX = 'tile_',
        PREFIX_LENGTH = ID_PREFIX.length,
        imageDiv = null,
        activeTiles = {},
        currentTiles = {};

    function createImageContainer() {
        imageDiv = DOM.create('div', 't5-tiles', DOM.styles({
            width: panFrame.offsetWidth + 'px',
            height: panFrame.offsetHeight + 'px'
        }));

        // append the panFrame to the same element as the canvas
        if (panFrame.childNodes.length > 0) {
            panFrame.insertBefore(imageDiv, panFrame.childNodes[0]);
        }
        else {
            panFrame.appendChild(imageDiv);
        } // if..else

        view.attachFrame(imageDiv);
    } // createImageContainer

    function createTileImage(tile) {
        // create the image
        var image = tile.image = new Image();

        // save to the tile cache so we can remove it once no longer needed
        activeTiles[tile.id] = tile;

        // set the image load handler
        image.onload = function() {
            if (currentTiles[tile.id]) {
                // check that this image is still valid (it will be in the tile cache)
                imageDiv.appendChild(this);
            }
            // otherwise, reset the image
            else {
                tile.image = null;
            } // if..else
        };

        // initialise the image source
        image.src = tile.url;

        // initialise the image style
        image.style.cssText = '-webkit-user-select: none; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; position: absolute;';

        // return the image
        return image;
    }

    function handleDetach() {
        // remove the image div from the panFrame
        panFrame.removeChild(imageDiv);
    } // handleDetach

    function handlePredraw(evt, layers, viewport, tickcount, hits) {
        // remove old tiles
        removeOldObjects(activeTiles, currentTiles);
        currentTiles = {};
    } // handlePredraw

    function handleReset(evt) {
        removeOldObjects(activeTiles, currentTiles = {});

        // remove all the children of the image div (just to be sure)
        while (imageDiv.childNodes.length > 0) {
            imageDiv.removeChild(imageDiv.childNodes[0]);
        } // while
    } // handleReset

    function removeOldObjects(activeObj, currentObj, flagField) {
        var deletedKeys = [];

        // iterate through the active objects
        // TODO: use something other than a for in loop please...
        for (var objId in activeObj) {
            var item = activeObj[objId],
                inactive = flagField ? item[flagField] : (! currentObj[objId]);

            // if the object is not in the current objects, remove from the scene
            if (inactive) {
                if (item.image && item.image.parentNode) {
                    // reset the image src
                    item.image.src = '';

                    // remove the image from the dom
                    imageDiv.removeChild(item.image);

                    // reset to null
                    item.image = null;
                } // if

                // add to the deleted keys
                deletedKeys[deletedKeys.length] = objId;
            } // if
        } // for

        // remove the deleted keys from the active objects
        for (var ii = deletedKeys.length; ii--; ) {
            delete activeObj[deletedKeys[ii]];
        } // for
    } // removeOldObjects

    /* exports */

    function drawTiles(viewport, tiles, okToLoad) {
        var tile,
            image,
            offsetX = viewport.x,
            offsetY = viewport.y;

        // draw the tiles
        for (var ii = tiles.length; ii--; ) {
            tile = tiles[ii];

            if (tile.url) {
                // get the tile image
                image = tile.image || (okToLoad ? createTileImage(tile) : null);

                // if we have an image, then move it
                if (image) {
                    DOM.move(image, tile.x - offsetX, tile.y - offsetY);
                } // if

                // flag the tile as current
                currentTiles[tile.id] = tile;
            } // if
        } // for
    } // drawTiles

    /* initialization */

    // attach the background image display
    createImageContainer();

    var _this = _extend(baseRenderer, {
        drawTiles: drawTiles
    });

    // handle the predraw
    _this.bind('predraw', handlePredraw);
    _this.bind('detach', handleDetach);
    view.bind('reset', handleReset);

    return _this;
});<commit_msg>Removed image src reset<commit_after>/**
# RENDERER: dom
*/
reg('renderer', 'dom', function(view, panFrame, container, params, baseRenderer) {

    /* internals */

    var ID_PREFIX = 'tile_',
        PREFIX_LENGTH = ID_PREFIX.length,
        imageDiv = null,
        activeTiles = {},
        currentTiles = {};

    function createImageContainer() {
        imageDiv = DOM.create('div', 't5-tiles', DOM.styles({
            width: panFrame.offsetWidth + 'px',
            height: panFrame.offsetHeight + 'px'
        }));

        // append the panFrame to the same element as the canvas
        if (panFrame.childNodes.length > 0) {
            panFrame.insertBefore(imageDiv, panFrame.childNodes[0]);
        }
        else {
            panFrame.appendChild(imageDiv);
        } // if..else

        view.attachFrame(imageDiv);
    } // createImageContainer

    function createTileImage(tile) {
        // create the image
        var image = tile.image = new Image();

        // save to the tile cache so we can remove it once no longer needed
        activeTiles[tile.id] = tile;

        // set the image load handler
        image.onload = function() {
            if (currentTiles[tile.id]) {
                // check that this image is still valid (it will be in the tile cache)
                imageDiv.appendChild(this);
            }
            // otherwise, reset the image
            else {
                tile.image = null;
            } // if..else
        };

        // initialise the image source
        image.src = tile.url;

        // initialise the image style
        image.style.cssText = '-webkit-user-select: none; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; position: absolute;';

        // return the image
        return image;
    }

    function handleDetach() {
        // remove the image div from the panFrame
        panFrame.removeChild(imageDiv);
    } // handleDetach

    function handlePredraw(evt, layers, viewport, tickcount, hits) {
        // remove old tiles
        removeOldObjects(activeTiles, currentTiles);
        currentTiles = {};
    } // handlePredraw

    function handleReset(evt) {
        removeOldObjects(activeTiles, currentTiles = {});

        // remove all the children of the image div (just to be sure)
        while (imageDiv.childNodes.length > 0) {
            imageDiv.removeChild(imageDiv.childNodes[0]);
        } // while
    } // handleReset

    function removeOldObjects(activeObj, currentObj, flagField) {
        var deletedKeys = [];

        // iterate through the active objects
        // TODO: use something other than a for in loop please...
        for (var objId in activeObj) {
            var item = activeObj[objId],
                inactive = flagField ? item[flagField] : (! currentObj[objId]);

            // if the object is not in the current objects, remove from the scene
            if (inactive) {
                if (item.image && item.image.parentNode) {
                    // reset the image src
                    delete item.image.src;

                    // remove the image from the dom
                    imageDiv.removeChild(item.image);

                    // reset to null
                    delete item.image;
                } // if

                // add to the deleted keys
                deletedKeys[deletedKeys.length] = objId;
            } // if
        } // for

        // remove the deleted keys from the active objects
        for (var ii = deletedKeys.length; ii--; ) {
            delete activeObj[deletedKeys[ii]];
        } // for
    } // removeOldObjects

    /* exports */

    function drawTiles(viewport, tiles, okToLoad) {
        var tile,
            image,
            offsetX = viewport.x,
            offsetY = viewport.y;

        // draw the tiles
        for (var ii = tiles.length; ii--; ) {
            tile = tiles[ii];

            if (tile.url) {
                // get the tile image
                image = tile.image || (okToLoad ? createTileImage(tile) : null);

                // if we have an image, then move it
                if (image) {
                    DOM.move(image, tile.x - offsetX, tile.y - offsetY);
                } // if

                // flag the tile as current
                currentTiles[tile.id] = tile;
            } // if
        } // for
    } // drawTiles

    /* initialization */

    // attach the background image display
    createImageContainer();

    var _this = _extend(baseRenderer, {
        drawTiles: drawTiles
    });

    // handle the predraw
    _this.bind('predraw', handlePredraw);
    _this.bind('detach', handleDetach);
    view.bind('reset', handleReset);

    return _this;
});<|endoftext|>