<commit_before>const path = require('path')
const services = require('src/services')
const proc = require('src/proc')
const config = require('src/config')
const fixture = require('test/fixtures/service')

describe('services', () => {
  afterEach(() => {
    services.running = []
  })
  describe('getStopTimeSecs', () => {
    it('uses the service config stopTimeSecs property if exists', () => {
      const cfg = { stopTimeSecs: 3 }
      const svc = { stopTimeSecs: 5 }
      expect(services.getStopTimeSecs(cfg, svc)).to.equal(5)
    })
    it('uses the global config stopTimeSecs property if exists and no service config is set', () => {
      const cfg = { stopTimeSecs: 3 }
      const svc = {}
      expect(services.getStopTimeSecs(cfg, svc)).to.equal(3)
    })
    it('uses the system default if neither global or service config props are set', () => {
      expect(services.getStopTimeSecs({}, {})).to.equal(10)
    })
  })
  describe('get', () => {
    before(() => {
      config.defaultPath = path.resolve(__dirname, '../fixtures/devlab.yml')
    })
    it('returns false if no services are specified', () => {
      expect(services.get({})).to.be.false()
    })
    it('returns an array of services and their command arrays', () => {
      const svc = services.get(config.load())
      expect(svc[0].name).to.equal('mongodb')
      expect(svc[0].args).to.deep.equal(['run', '-d', '--rm', '--privileged', '-p', '27017:27017', '--name', 'dl_mongodb_test', 'mongo:3.0'])
    })
  })
  describe('run', () => {
    let procRunStub
    afterEach(() => {
      if (proc.exec.restore) proc.exec.restore()
      procRunStub.restore()
    })
    it('starts all non-running services', () => {
      global.instanceId = 'test'
      sinon.stub(proc, 'exec', (cmd) => { /* eslint no-undef: 0 */
        if (cmd === 'docker ps -f name=dl_redis_test -q') return Promise.resolve('123456')
        return Promise.resolve(undefined)
      })
      procRunStub = sinon.stub(proc, 'run', () => Promise.resolve())
      return services.run(fixture).then(() => {
        expect(services.running).to.deep.equal([{ name: 'dl_mongodb_test', stopTimeSecs: 10 }])
      })
    })
    it('rejects when a service fails to start', () => {
      procRunStub = sinon.stub(proc, 'run', () => Promise.reject())
      return services.run([{ name: 'fart', args: ['foo'] }])
        .then(() => {
          throw new Error('Should have failed')
        })
        .catch(() => {
          expect(services.running.length).to.equal(0)
        })
    })
  })
  describe('stop', () => {
    let procRunStub
    beforeEach(() => {
      procRunStub = sinon.stub(proc, 'run', (svc) => Promise.resolve().then(() => {
        if (svc === 'dl_fail_test') {
          const testError = new Error()
          testError.svcs = ['dl_fail_test']
          throw testError
        }
        return
      }))
    })
    afterEach(() => {
      procRunStub.restore()
    })
    it('does nothing if no services are running', () => {
      services.running = []
      return services.stop()
        .then(() => {
          expect(procRunStub).to.not.be.called()
        })
    })
    it('resolves after calling proc.run with stop command for running services', () => {
      services.running = [{ name: 'dl_foo_test', stopTimeSecs: 10 }, { name: 'dl_bar_test', stopTimeSecs: 10 }]
      return services.stop()
        .then(() => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_foo_test', ''])
          expect(procRunStub.getCalls()[1].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_bar_test', ''])
        })
    })
    it('resolves after calling proc with stop and rm only for non-persistent services', () => {
      services.running = [{ name: 'dl_foo_test', stopTimeSecs: 10 }, { name: 'bar' }]
      return services.stop()
        .then(() => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_foo_test', ''])
        })
    })
    it('rejects with error containing names of services that failed', () => {
      services.running = ['dl_foo_test', 'dl_fail_test']
      return services.stop()
        .catch((err) => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', 'dl_foo_test'])
          expect(err.svcs).to.deep.equal(['dl_fail_test'])
        })
    })
  })
  describe('filterEnabled', () => {
    afterEach(() => {
      services.disabled = []
    })
    it('does nothing if no task is supplied', () => {
      const cfg = { run: [] }
      expect(services.filterEnabled(cfg)).to.deep.equal(cfg)
    })
    it('does nothing if single task config is not an object', () => {
      const cfg = { run: ['test'], tasks: { test: 'echo "not an obj"' } }
      expect(services.filterEnabled(cfg)).to.deep.equal(cfg)
    })
    it('does nothing if any task config is not an object', () => {
      const cfg = { run: ['string', 'obj'], tasks: { string: 'echo "not an obj"', obj: { disable: '*' } } }
      expect(services.filterEnabled(cfg)).to.deep.equal(cfg)
    })
    it('disables all services when \'*\' is supplied', () => {
      const cfg = {
        services: [{ disabledOne: { from: 'test' } }, { disabledTwo: { from: 'disable' } }],
        tasks: { test: { disable: '*', cmd: 'echo hello' } },
        run: ['test']
      }
      expect(services.filterEnabled(cfg).services).to.deep.equal([])
      expect(services.disabled).to.deep.equal(['disabledOne', 'disabledTwo'])
    })
    it('returns config with filtered services array', () => {
      const cfg = {
        services: [{ keep: { from: 'test' } }, { disable: { from: 'disable' } }],
        tasks: { test: { disable: ['disable'], cmd: 'echo hello' } },
        run: ['test']
      }
      expect(services.filterEnabled(cfg).services).to.deep.equal([{ keep: { from: 'test' } }])
      expect(services.disabled[0]).to.equal('disable')
    })
    it('disables services shared between chained tasks', () => {
      const cfg = {
        services: [{ shared: { from: 'test' } }, { onlyTest: { from: 'onlyTest' } }],
        tasks: { test: { disable: '*', cmd: 'echo hello' }, lint: { disable: ['shared'], cmd: 'lint' } },
        run: ['test', 'lint']
      }
      expect(services.filterEnabled(cfg).services).to.deep.equal([{ onlyTest: { from: 'onlyTest' } }])
      expect(services.disabled[0]).to.equal('shared')
    })
  })
})
<commit_msg>refactor(services.spec): add rmOnShutdown coverage<commit_after>const path = require('path')
const services = require('src/services')
const proc = require('src/proc')
const config = require('src/config')
const fixture = require('test/fixtures/service')

describe('services', () => {
  afterEach(() => {
    if (global.rmOnShutdown) delete global.rmOnShutdown
    services.running = []
  })
  describe('getStopTimeSecs', () => {
    it('uses the service config stopTimeSecs property if exists', () => {
      const cfg = { stopTimeSecs: 3 }
      const svc = { stopTimeSecs: 5 }
      expect(services.getStopTimeSecs(cfg, svc)).to.equal(5)
    })
    it('uses the global config stopTimeSecs property if exists and no service config is set', () => {
      const cfg = { stopTimeSecs: 3 }
      const svc = {}
      expect(services.getStopTimeSecs(cfg, svc)).to.equal(3)
    })
    it('uses the system default if neither global or service config props are set', () => {
      expect(services.getStopTimeSecs({}, {})).to.equal(10)
    })
  })
  describe('get', () => {
    before(() => {
      config.defaultPath = path.resolve(__dirname, '../fixtures/devlab.yml')
    })
    it('returns false if no services are specified', () => {
      expect(services.get({})).to.be.false()
    })
    it('returns an array of services and their command arrays', () => {
      const svc = services.get(config.load())
      expect(svc[0].name).to.equal('mongodb')
      expect(svc[0].args).to.deep.equal(['run', '-d', '--rm', '--privileged', '-p', '27017:27017', '--name', 'dl_mongodb_test', 'mongo:3.0'])
    })
    it('returns an array of services and their command arrays (with rmOnShutdown)', () => {
      global.rmOnShutdown = true
      const svc = services.get(config.load())
      expect(svc[0].name).to.equal('mongodb')
      expect(svc[0].args).to.deep.equal(['run', '-d', '--privileged', '-p', '27017:27017', '--name', 'dl_mongodb_test', 'mongo:3.0'])
    })
  })
  describe('run', () => {
    let procRunStub
    afterEach(() => {
      if (proc.exec.restore) proc.exec.restore()
      procRunStub.restore()
    })
    it('starts all non-running services', () => {
      global.instanceId = 'test'
      sinon.stub(proc, 'exec', (cmd) => { /* eslint no-undef: 0 */
        if (cmd === 'docker ps -f name=dl_redis_test -q') return Promise.resolve('123456')
        return Promise.resolve(undefined)
      })
      procRunStub = sinon.stub(proc, 'run', () => Promise.resolve())
      return services.run(fixture).then(() => {
        expect(services.running).to.deep.equal([{ name: 'dl_mongodb_test', stopTimeSecs: 10 }])
      })
    })
    it('rejects when a service fails to start', () => {
      procRunStub = sinon.stub(proc, 'run', () => Promise.reject())
      return services.run([{ name: 'fart', args: ['foo'] }])
        .then(() => {
          throw new Error('Should have failed')
        })
        .catch(() => {
          expect(services.running.length).to.equal(0)
        })
    })
  })
  describe('stop', () => {
    let procRunStub
    beforeEach(() => {
      procRunStub = sinon.stub(proc, 'run', (svc) => Promise.resolve().then(() => {
        if (svc === 'dl_fail_test') {
          const testError = new Error()
          testError.svcs = ['dl_fail_test']
          throw testError
        }
        return
      }))
    })
    afterEach(() => {
      procRunStub.restore()
    })
    it('does nothing if no services are running', () => {
      services.running = []
      return services.stop()
        .then(() => {
          expect(procRunStub).to.not.be.called()
        })
    })
    it('resolves after calling proc.run with stop command for running services', () => {
      services.running = [{ name: 'dl_foo_test', stopTimeSecs: 10 }, { name: 'dl_bar_test', stopTimeSecs: 10 }]
      return services.stop()
        .then(() => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_foo_test'])
          expect(procRunStub.getCalls()[1].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_bar_test'])
        })
    })
    it('resolves after calling proc.run with stop and rm commands', () => {
      global.rmOnShutdown = true
      services.running = [{ name: 'dl_foo_test', stopTimeSecs: 10 }, { name: 'dl_bar_test', stopTimeSecs: 10 }]
      return services.stop()
        .then(() => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_foo_test'])
          expect(procRunStub.getCalls()[1].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_bar_test'])
          expect(procRunStub.getCalls()[2].args[0]).to.deep.equal(['rm', 'dl_foo_test'])
          expect(procRunStub.getCalls()[3].args[0]).to.deep.equal(['rm', 'dl_bar_test'])
        })
    })
    it('resolves after calling proc with stop and rm only for non-persistent services', () => {
      services.running = [{ name: 'dl_foo_test', stopTimeSecs: 10 }, { name: 'bar' }]
      return services.stop()
        .then(() => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', '-t', 10, 'dl_foo_test'])
        })
    })
    it('rejects with error containing names of services that failed', () => {
      services.running = ['dl_foo_test', 'dl_fail_test']
      return services.stop()
        .catch((err) => {
          expect(procRunStub.getCalls()[0].args[0]).to.deep.equal(['stop', 'dl_foo_test'])
          expect(err.svcs).to.deep.equal(['dl_fail_test'])
        })
    })
  })
  describe('filterEnabled', () => {
    afterEach(() => {
      services.disabled = []
    })
    it('does nothing if no task is supplied', () => {
      const cfg = { run: [] }
      expect(services.filterEnabled(cfg)).to.deep.equal(cfg)
    })
    it('does nothing if single task config is not an object', () => {
      const cfg = { run: ['test'], tasks: { test: 'echo "not an obj"' } }
      expect(services.filterEnabled(cfg)).to.deep.equal(cfg)
    })
    it('does nothing if any task config is not an object', () => {
      const cfg = { run: ['string', 'obj'], tasks: { string: 'echo "not an obj"', obj: { disable: '*' } } }
      expect(services.filterEnabled(cfg)).to.deep.equal(cfg)
    })
    it('disables all services when \'*\' is supplied', () => {
      const cfg = {
        services: [{ disabledOne: { from: 'test' } }, { disabledTwo: { from: 'disable' } }],
        tasks: { test: { disable: '*', cmd: 'echo hello' } },
        run: ['test']
      }
      expect(services.filterEnabled(cfg).services).to.deep.equal([])
      expect(services.disabled).to.deep.equal(['disabledOne', 'disabledTwo'])
    })
    it('returns config with filtered services array', () => {
      const cfg = {
        services: [{ keep: { from: 'test' } }, { disable: { from: 'disable' } }],
        tasks: { test: { disable: ['disable'], cmd: 'echo hello' } },
        run: ['test']
      }
      expect(services.filterEnabled(cfg).services).to.deep.equal([{ keep: { from: 'test' } }])
      expect(services.disabled[0]).to.equal('disable')
    })
    it('disables services shared between chained tasks', () => {
      const cfg = {
        services: [{ shared: { from: 'test' } }, { onlyTest: { from: 'onlyTest' } }],
        tasks: { test: { disable: '*', cmd: 'echo hello' }, lint: { disable: ['shared'], cmd: 'lint' } },
        run: ['test', 'lint']
      }
      expect(services.filterEnabled(cfg).services).to.deep.equal([{ onlyTest: { from: 'onlyTest' } }])
      expect(services.disabled[0]).to.equal('shared')
    })
  })
})
<|endoftext|>


<commit_before>/**
 * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。
 */

define([
    '../base',
    '../uploader',
    '../file',
    './widget'
], function( Base, Uploader, WUFile ) {

    var $ = Base.$,
        validators = {},
        api;

    /**
     * @event error
     * @param {String} type 错误类型。
     * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。
     *
     * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。
     * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。
     * @for  Uploader
     */

    // 暴露给外面的api
    api = {

        // 添加验证器
        addValidator: function( type, cb ) {
            validators[ type ] = cb;
        },

        // 移除验证器
        removeValidator: function( type ) {
            delete validators[ type ];
        }
    };

    // 在Uploader初始化的时候启动Validators的初始化
    Uploader.register({
        init: function() {
            var me = this;
            Base.nextTick(function() {
                $.each( validators, function() {
                    this.call( me.owner );
                });
            });
        }
    });

    /**
     * @property {int} [fileNumLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证文件总数量, 超出则不允许加入队列。
     */
    api.addValidator( 'fileNumLimit', function() {
        var uploader = this,
            opts = uploader.options,
            count = 0,
            max = parseInt( opts.fileNumLimit, 10 ),
            flag = true;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {

            if ( count >= max && flag ) {
                flag = false;
                this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );
                setTimeout(function() {
                    flag = true;
                }, 1 );
            }

            return count >= max ? false : true;
        });

        uploader.on( 'fileQueued', function() {
            count++;
        });

        uploader.on( 'fileDequeued', function() {
            count--;
        });

        uploader.on( 'uploadFinished reset', function() {
            count = 0;
        });
    });

    /**
     * @property {int} [fileSizeLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。
     */
    api.addValidator( 'fileSizeLimit', function() {
        var uploader = this,
            opts = uploader.options,
            count = 0,
            max = opts.fileSizeLimit >> 0,
            flag = true;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {
            var invalid = count + file.size > max;

            if ( invalid && flag ) {
                flag = false;
                this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );
                setTimeout(function() {
                    flag = true;
                }, 1 );
            }

            return invalid ? false : true;
        });

        uploader.on( 'fileQueued', function( file ) {
            count += file.size;
        });

        uploader.on( 'fileDequeued', function( file ) {
            count -= file.size;
        });

        uploader.on( 'uploadFinished reset', function() {
            count = 0;
        });
    });

    /**
     * @property {int} [fileSingleSizeLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。
     */
    api.addValidator( 'fileSingleSizeLimit', function() {
        var uploader = this,
            opts = uploader.options,
            max = opts.fileSingleSizeLimit;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {

            if ( file.size > max ) {
                file.setStatus( WUFile.Status.INVALID, 'exceed_size' );
                this.trigger( 'error', 'F_EXCEED_SIZE', file );
                return false;
            }

        });

    });

    /**
     * @property {int} [duplicate=undefined]
     * @namespace options
     * @for Uploader
     * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.
     */
    api.addValidator( 'duplicate', function() {
        var uploader = this,
            opts = uploader.options,
            mapping = {};

        if ( opts.duplicate ) {
            return;
        }

        function hashString( str ) {
            var hash = 0,
                i = 0,
                len = str.length,
                _char;

            for ( ; i < len; i++ ) {
                _char = str.charCodeAt( i );
                hash = _char + (hash << 6) + (hash << 16) - hash;
            }

            return hash;
        }

        uploader.on( 'beforeFileQueued', function( file ) {
            var hash = file.__hash || (file.__hash = hashString( file.name +
                    file.size + file.lastModifiedDate ));

            // 已经重复了
            if ( mapping[ hash ] ) {
                this.trigger( 'error', 'F_DUPLICATE', file );
                return false;
            }
        });

        uploader.on( 'fileQueued', function( file ) {
            var hash = file.__hash;

            hash && (mapping[ hash ] = true);
        });

        uploader.on( 'fileDequeued', function( file ) {
            var hash = file.__hash;

            hash && (delete mapping[ hash ]);
        });

        uploader.on( 'reset', function() {
            mapping = {};
        });
    });

    return api;
});
<commit_msg>fix https://github.com/fex-team/webuploader/issues/282<commit_after>/**
 * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。
 */

define([
    '../base',
    '../uploader',
    '../file',
    './widget'
], function( Base, Uploader, WUFile ) {

    var $ = Base.$,
        validators = {},
        api;

    /**
     * @event error
     * @param {String} type 错误类型。
     * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。
     *
     * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。
     * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。
     * @for  Uploader
     */

    // 暴露给外面的api
    api = {

        // 添加验证器
        addValidator: function( type, cb ) {
            validators[ type ] = cb;
        },

        // 移除验证器
        removeValidator: function( type ) {
            delete validators[ type ];
        }
    };

    // 在Uploader初始化的时候启动Validators的初始化
    Uploader.register({
        init: function() {
            var me = this;
            Base.nextTick(function() {
                $.each( validators, function() {
                    this.call( me.owner );
                });
            });
        }
    });

    /**
     * @property {int} [fileNumLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证文件总数量, 超出则不允许加入队列。
     */
    api.addValidator( 'fileNumLimit', function() {
        var uploader = this,
            opts = uploader.options,
            count = 0,
            max = parseInt( opts.fileNumLimit, 10 ),
            flag = true;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {

            if ( count >= max && flag ) {
                flag = false;
                this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );
                setTimeout(function() {
                    flag = true;
                }, 1 );
            }

            return count >= max ? false : true;
        });

        uploader.on( 'fileQueued', function() {
            count++;
        });

        uploader.on( 'fileDequeued', function() {
            count--;
        });

        uploader.on( 'reset', function() {
            count = 0;
        });
    });

    /**
     * @property {int} [fileSizeLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。
     */
    api.addValidator( 'fileSizeLimit', function() {
        var uploader = this,
            opts = uploader.options,
            count = 0,
            max = opts.fileSizeLimit >> 0,
            flag = true;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {
            var invalid = count + file.size > max;

            if ( invalid && flag ) {
                flag = false;
                this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );
                setTimeout(function() {
                    flag = true;
                }, 1 );
            }

            return invalid ? false : true;
        });

        uploader.on( 'fileQueued', function( file ) {
            count += file.size;
        });

        uploader.on( 'fileDequeued', function( file ) {
            count -= file.size;
        });

        uploader.on( 'reset', function() {
            count = 0;
        });
    });

    /**
     * @property {int} [fileSingleSizeLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。
     */
    api.addValidator( 'fileSingleSizeLimit', function() {
        var uploader = this,
            opts = uploader.options,
            max = opts.fileSingleSizeLimit;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {

            if ( file.size > max ) {
                file.setStatus( WUFile.Status.INVALID, 'exceed_size' );
                this.trigger( 'error', 'F_EXCEED_SIZE', file );
                return false;
            }

        });

    });

    /**
     * @property {int} [duplicate=undefined]
     * @namespace options
     * @for Uploader
     * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.
     */
    api.addValidator( 'duplicate', function() {
        var uploader = this,
            opts = uploader.options,
            mapping = {};

        if ( opts.duplicate ) {
            return;
        }

        function hashString( str ) {
            var hash = 0,
                i = 0,
                len = str.length,
                _char;

            for ( ; i < len; i++ ) {
                _char = str.charCodeAt( i );
                hash = _char + (hash << 6) + (hash << 16) - hash;
            }

            return hash;
        }

        uploader.on( 'beforeFileQueued', function( file ) {
            var hash = file.__hash || (file.__hash = hashString( file.name +
                    file.size + file.lastModifiedDate ));

            // 已经重复了
            if ( mapping[ hash ] ) {
                this.trigger( 'error', 'F_DUPLICATE', file );
                return false;
            }
        });

        uploader.on( 'fileQueued', function( file ) {
            var hash = file.__hash;

            hash && (mapping[ hash ] = true);
        });

        uploader.on( 'fileDequeued', function( file ) {
            var hash = file.__hash;

            hash && (delete mapping[ hash ]);
        });

        uploader.on( 'reset', function() {
            mapping = {};
        });
    });

    return api;
});
<|endoftext|>


<commit_before>// 50: Generator - iterator
// To do: make all tests pass, leave the assert lines unchanged!

describe('a generator returns an iterable object', function() {

  function* generatorFunction(){
    yield 1;
    yield 2;
  }

  let generator;

  beforeEach(() => {
    generator = generatorFunction();
  });

  it('a generator returns an object', function() {
    const typeOfTheGenerator = '';
    assert.equal(typeof generator, typeOfTheGenerator);
  });

  it('a generator object has a key `Symbol.iterator`', function() {
    const key = '???';
    assert.equal(key in generator, true);
  });

  it('the `Symbol.iterator` is a function', function() {
    const theType = typeof generator.Symbol.iterator;
    assert.equal(theType, 'function');
  });

  it('can be looped with `for-of`, which expects an iterable', function() {
    function iterateForOf(){
      for (let value of {}) {
        // no statements needed
      }
    }
    assert.doesNotThrow(iterateForOf);
  });

});
<commit_msg>Kataify.<commit_after>// 50: Generator - iterator
// To do: make all tests pass, leave the assert lines unchanged!

describe('Generators returns an iterable object', function() {
  function* generatorFunction(){
    yield 1;
    yield 2;
  }
  let generator;
  beforeEach(() => {
    generator = generatorFunction();
  });
  it('a generator returns an object', function() {
    //// const typeOfTheGenerator = '';
    const typeOfTheGenerator = 'object';
    assert.equal(typeof generator, typeOfTheGenerator);
  });
  it('a generator object has a key `Symbol.iterator`', function() {
    //// const key = '???';
    const key = Symbol.iterator;
    assert.equal(key in generator, true);
  });
  it('the `Symbol.iterator` is a function', function() {
    //// const theType = typeof generator.Symbol.iterator;
    const theType = typeof generator[Symbol.iterator];
    assert.equal(theType, 'function');
  });
  it('can be looped with `for-of`, which expects an iterable', function() {
    function iterateForOf(){
      //// for (let value of {}) {
      for (let value of generator) {
        // no statements needed
      }
    }
    assert.doesNotThrow(iterateForOf);
  });
});
<|endoftext|>


<commit_before>/**
 * Estonian translation for bootstrap-datepicker
 * Ando Roots <https://github.com/anroots>
 * Fixes by Illimar Tambek <<https://github.com/ragulka>
 */
;(function($){
  $.fn.datepicker.dates['et'] = {
    days: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev", "Pühapäev"],
    daysShort: ["Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup", "Pühap"],
    daysMin: ["P", "E", "T", "K", "N", "R", "L", "P"],
    months: ["Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"],
    monthsShort: ["Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni", "Juuli", "Aug", "Sept", "Okt", "Nov", "Dets"],
    today: "Täna",
    today: "Tühjenda",
    weekStart: 1,
    format: "dd.mm.yyyy"
  };
}(jQuery));
<commit_msg>Fix identation<commit_after>/**
 * Estonian translation for bootstrap-datepicker
 * Ando Roots <https://github.com/anroots>
 * Fixes by Illimar Tambek <<https://github.com/ragulka>
 */
;(function($){
	$.fn.datepicker.dates['et'] = {
		days: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev", "Pühapäev"],
		daysShort: ["Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup", "Pühap"],
		daysMin: ["P", "E", "T", "K", "N", "R", "L", "P"],
		months: ["Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"],
		monthsShort: ["Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni", "Juuli", "Aug", "Sept", "Okt", "Nov", "Dets"],
		today: "Täna",
		today: "Tühjenda",
		weekStart: 1,
		format: "dd.mm.yyyy"
	};
}(jQuery));
<|endoftext|>


<commit_before>import React, { Component, createElement } from 'react'
import PropTypes from 'prop-types'
import ResizeObserver from 'resize-observer-polyfill'
import getTypes from './get-types'
import getContentRect from './get-content-rect'

function withContentRect(types) {
  return WrappedComponent =>
    class extends Component {
      static propTypes = {
        client: PropTypes.bool,
        offset: PropTypes.bool,
        scroll: PropTypes.bool,
        bounds: PropTypes.bool,
        margin: PropTypes.bool,
        innerRef: PropTypes.func,
        onResize: PropTypes.func,
        children: PropTypes.func,
      }

      state = {
        contentRect: {
          entry: {},
          client: {},
          offset: {},
          scroll: {},
          bounds: {},
          margin: {},
        },
      }

      componentWillMount() {
        this._resizeObserver = new ResizeObserver(this.measure)
      }

      measure = entries => {
        const contentRect = getContentRect(
          this._node,
          types || getTypes(this.props)
        )

        if (entries) {
          contentRect.entry = entries[0].contentRect
        }

        this.setState({ contentRect })

        if (typeof this.props.onResize === 'function') {
          this.props.onResize(contentRect)
        }
      }

      _handleRef = node => {
        this._node = node

        if (this._resizeObserver) {
          if (node) {
            this._resizeObserver.observe(node)
          } else {
            this._resizeObserver.disconnect(node)
          }
        }

        if (typeof this.props.innerRef === 'function') {
          this.props.innerRef(node)
        }
      }

      render() {
        const { innerRef, onResize, ...props } = this.props
        return createElement(WrappedComponent, {
          ...props,
          measureRef: this._handleRef,
          measure: this.measure,
          contentRect: this.state.contentRect,
        })
      }
    }
}

export default withContentRect
<commit_msg>Disconnect correct node within `handleRef`<commit_after>import React, { Component, createElement } from 'react'
import PropTypes from 'prop-types'
import ResizeObserver from 'resize-observer-polyfill'
import getTypes from './get-types'
import getContentRect from './get-content-rect'

function withContentRect(types) {
  return WrappedComponent =>
    class extends Component {
      static propTypes = {
        client: PropTypes.bool,
        offset: PropTypes.bool,
        scroll: PropTypes.bool,
        bounds: PropTypes.bool,
        margin: PropTypes.bool,
        innerRef: PropTypes.func,
        onResize: PropTypes.func,
        children: PropTypes.func,
      }

      state = {
        contentRect: {
          entry: {},
          client: {},
          offset: {},
          scroll: {},
          bounds: {},
          margin: {},
        },
      }

      componentWillMount() {
        this._resizeObserver = new ResizeObserver(this.measure)
      }

      measure = entries => {
        const contentRect = getContentRect(
          this._node,
          types || getTypes(this.props)
        )

        if (entries) {
          contentRect.entry = entries[0].contentRect
        }

        this.setState({ contentRect })

        if (typeof this.props.onResize === 'function') {
          this.props.onResize(contentRect)
        }
      }

      _handleRef = node => {
        if (this._resizeObserver) {
          if (node) {
            this._resizeObserver.observe(node)
          } else {
            this._resizeObserver.disconnect(this._node)
          }
        }
        this._node = node

        if (typeof this.props.innerRef === 'function') {
          this.props.innerRef(node)
        }
      }

      render() {
        const { innerRef, onResize, ...props } = this.props
        return createElement(WrappedComponent, {
          ...props,
          measureRef: this._handleRef,
          measure: this.measure,
          contentRect: this.state.contentRect,
        })
      }
    }
}

export default withContentRect
<|endoftext|>


<commit_before>var urlParser = require('util/urlParser.js')

function autocomplete (input, strings) {
  // if there is text after the selection, we can never autocomplete
  if (input.selectionEnd !== input.value.length) {
    return {
      valid: false
    }
  }

  var text = input.value.substring(0, input.selectionStart)

  for (var i = 0; i < strings.length; i++) {
    // check if the item can be autocompleted
    if (strings[i].toLowerCase().indexOf(text.toLowerCase()) === 0) {
      input.value = strings[i]
      input.setSelectionRange(text.length, strings[i].length)

      return {
        valid: true,
        matchIndex: i
      }
    }
  }
  return {
    valid: false
  }
}

// autocompletes based on a result item
// returns: 1 - the exact URL was autocompleted, 0 - the domain was autocompleted, -1: nothing was autocompleted
function autocompleteURL (item, input) {
  var url = new URL(item.url)
  var hostname = url.hostname

  // the different variations of the URL we can autocomplete
  var possibleAutocompletions = [
    // we start with the domain
    hostname,
    // if that doesn't match, try the hostname without the www instead. The regex requires a slash at the end, so we add one, run the regex, and then remove it
    (hostname + '/').replace(urlParser.startingWWWRegex, '$1').replace('/', ''),
    // then try the whole URL
    urlParser.prettyURL(item.url),
    // then try the URL with querystring
    urlParser.basicURL(item.url),
    // then just try the URL with protocol
    item.url
  ]

  var autocompleteResult = autocomplete(input, possibleAutocompletions)

  if (!autocompleteResult.valid) {
    return -1
  } else if (autocompleteResult.matchIndex < 2 && url.pathname !== '/') {
    return 0
  } else {
    return 1
  }
}

module.exports = {autocomplete, autocompleteURL}
<commit_msg>include ports in domain autocompletions<commit_after>var urlParser = require('util/urlParser.js')

function autocomplete (input, strings) {
  // if there is text after the selection, we can never autocomplete
  if (input.selectionEnd !== input.value.length) {
    return {
      valid: false
    }
  }

  var text = input.value.substring(0, input.selectionStart)

  for (var i = 0; i < strings.length; i++) {
    // check if the item can be autocompleted
    if (strings[i].toLowerCase().indexOf(text.toLowerCase()) === 0) {
      input.value = strings[i]
      input.setSelectionRange(text.length, strings[i].length)

      return {
        valid: true,
        matchIndex: i
      }
    }
  }
  return {
    valid: false
  }
}

// autocompletes based on a result item
// returns: 1 - the exact URL was autocompleted, 0 - the domain was autocompleted, -1: nothing was autocompleted
function autocompleteURL (item, input) {
  var url = new URL(item.url)
  var hostname = url.hostname

  // the different variations of the URL we can autocomplete
  var possibleAutocompletions = [
    // we start with the domain, including any non-standard ports (such as localhost:8080)
    hostname + (url.port ? ':' + url.port : ''),
    // if that doesn't match, try the hostname without the www instead. The regex requires a slash at the end, so we add one, run the regex, and then remove it
    (hostname + '/').replace(urlParser.startingWWWRegex, '$1').replace('/', ''),
    // then try the whole URL
    urlParser.prettyURL(item.url),
    // then try the URL with querystring
    urlParser.basicURL(item.url),
    // then just try the URL with protocol
    item.url
  ]

  var autocompleteResult = autocomplete(input, possibleAutocompletions)

  if (!autocompleteResult.valid) {
    return -1
  } else if (autocompleteResult.matchIndex < 2 && url.pathname !== '/') {
    return 0
  } else {
    return 1
  }
}

module.exports = {autocomplete, autocompleteURL}
<|endoftext|>


<commit_before>/*jslint browser: true, plusplus: true */
/*global angular, jQuery, console */
/** @module Autonumeric
@icon calculator
@summary The autonumeric component wraps up the autoNumeric jQuery plugin to format any type of number, including currency.
 @description The `bb-autonumeric` directive wraps up the autoNumeric jQuery plugin to format any type of number, including currency. You must use this directive in conjunction with the `ngModel` directive where the property bound to `ngModel` is the raw numeric value on your model.

 ### Dependencies ###

 - **[autoNumeric](http://www.decorplanit.com/plugin/) (1.9.27 or higher)** Used to format money values

---

### Autonumeric Settings ###

 - `bb-autonumeric` This  can optionally be assigned the name of a property from the `bbAutonumericConfig` object.  If none is specified, it defaults to `number`.
 - `bb-autonumeric-settings` This can be assigned a value that represents a settings object that can be passed to autoNumeric.  These options will override any default options specified in the `bb-autonumeric` attribute.  A complete list of options is available [here](http://www.decorplanit.com/plugin/).

### Autonumeric Filter ###

In addition to the directive, there is also a filter that can be used to format numbers.  The filter has the added feature of optionally abbreviating a number according to Sky patterns.  For instance,
numbers over 10,000 will be displayed as 10k, over 1,000,000 as 1m, and 1,000,000,000 as 1b.  The filter takes three arguments:

 - `input` The value to format.
 - `configType` The name of the configuration (`number` or `money`) to apply to the value.
 - `abbreviate` A Boolean value indicating whether to abbreviate large numbers.
 */
(function ($) {
    'use strict';

    function getBaseSettings(bbAutoNumericConfig, configType) {
        var baseSettings,
            configSettings;

        baseSettings = angular.extend(
            {},
            $.fn.autoNumeric.defaults,
            bbAutoNumericConfig.number
        );

        if (configType) {
            configSettings = bbAutoNumericConfig[configType];
        }

        if (configSettings) {
            angular.extend(baseSettings, configSettings);
        }

        return baseSettings;
    }

    angular.module('sky.autonumeric', ['sky.resources', 'sky.window'])
        .constant('bbAutonumericConfig', {
            number: {
                aSep: ',',
                dGroup: 3,
                aDec: '.',
                pSign: 'p',
                mDec: 2
            },
            money: {
                aSign: '$'
            }
        })
        .directive('bbAutonumeric', ['$timeout', 'bbAutonumericConfig', 'bbWindow', function ($timeout, bbAutoNumericConfig, bbWindow) {
            return {
                require: 'ngModel',
                restrict: 'A',
                link: function ($scope, el, attrs, ngModel) {
                    var customSettings = {},
                        isIosUserAgent = bbWindow.isIosUserAgent();

                    function applySettings() {
                        el.autoNumeric('update', angular.extend({}, getBaseSettings(bbAutoNumericConfig, attrs.bbAutonumeric), customSettings));
                    }

                    function applyCssSettings(el) {
                        if (attrs.bbAutonumeric) {
                            el.addClass('bb-autonumeric-' + attrs.bbAutonumeric);
                        }
                    }

                    function autonumericChange() {
                        return $scope.$apply(function () {

                            var value = parseFloat(el.autoNumeric('get'));
                            console.log('in autonumeric change');

                            if (isNaN(value)) {
                                value = null;
                            }

                            console.log('end autonumeric change');
                            return ngModel.$setViewValue(value);
                        });
                    }

                    if (attrs.bbAutonumericSettings) {
                        $scope.$watch(attrs.bbAutonumericSettings, function (newValue) {
                            customSettings = newValue || {};
                            applySettings();
                        }, true);
                    }

                    el.autoNumeric(getBaseSettings(bbAutoNumericConfig, attrs.bbAutonumeric));
                    applyCssSettings(el);

                    // If a valid number, update the autoNumeric value.
                    // Also handles the model being updated, but being in correct (usually a paste).
                    // In that case, updates the model to what the autoNumeric plugin's value.
                    $scope.$watch(attrs.ngModel, function (newValue, oldValue) {
                        var getValue,
                            selectionStart;
                        if (newValue !== undefined && newValue !== null && !isNaN(newValue)) {

                            if (parseFloat(newValue) !== parseFloat(oldValue)) {

                                selectionStart = el[0].selectionStart;
                            }

                            el.autoNumeric('set', newValue);
                            getValue = el.autoNumeric('get');
                            if (parseFloat(getValue) !== parseFloat(newValue)) {
                                $timeout(autonumericChange);
                            } else if (el[0] && angular.isFunction(el[0].setSelectionRange) && angular.isDefined(selectionStart)) {
                                $timeout(function () {
                                    console.log('before set selection', selectionStart);
                                    console.log('function defined', angular.isFunction(el[0].setSelectionRange));
                                    el[0].setSelectionRange(selectionStart, selectionStart);
                                    console.log('after set selection');
                                });
                            }
                        } else if (newValue === null) {
                            el.val(null);

                        }
                    });

                    el.on('keydown', function (event) {
                        if (event.which === 13) {
                            autonumericChange();
                        }
                    });

                    el.on('change paste onpaste', function () {
                        autonumericChange();

                    });

                    // When focusing in textbox, select all.  This is to workaround not having placeholder text for autonumeric.
                    /*
                        istanbul ignore next: the test for this code isn't passing on IE 10 on BrowserStack in automated mode.
                        This isn't mission-critical so I'm just ignoring it for now.
                    */
                    el.on('focusin.bbAutonumeric', function () {
                        $timeout(function () {
                            // Check to ensure the field still has focus once the $timeout callback is executed.
                            // https://github.com/blackbaud/skyux/issues/64
                            if (el.is(':focus')) {
                                if (!isIosUserAgent) {
                                    el.select();
                                } else {
                                    //use setSelectionRange instead of select because select in a timeout does not work with iOS
                                    el[0].setSelectionRange(0, 9999);
                                }
                            }
                        });
                    });

                }
            };
        }])
        .filter('bbAutonumeric', ['bbAutonumericConfig', 'bbResources', function (bbAutonumericConfig, bbResources) {
            return function (input, configType, abbreviate) {
                var aSign,
                    dividend,
                    mDec,
                    formatted,
                    settings,
                    suffix,
                    tempEl;

                if (input === null || angular.isUndefined(input)) {
                    return '';
                }

                if (isNaN(input)) {
                    return input;
                }

                tempEl = $('');

                settings = getBaseSettings(bbAutonumericConfig, configType);

                if (abbreviate) {
                    if (settings.pSign === 's') {
                        // The suffix needs to go between the number and the currency symbol, so the currency
                        // symbol has to be left off and appended after the number is formatted.
                        aSign = settings.aSign;
                        settings.aSign = '';
                    }

                    input = Math.round(input);

                    if (input >= 1000000000) {
                        dividend = 100000000;
                        suffix = bbResources.autonumeric_abbr_billions;
                    } else if (input >= 1000000) {
                        dividend = 100000;
                        suffix = bbResources.autonumeric_abbr_millions;
                    } else if (input >= 10000) {
                        dividend = 100;
                        suffix = bbResources.autonumeric_abbr_thousands;
                    }

                    if (suffix) {
                        input = Math.floor(input / dividend) / 10;
                        mDec = Math.floor(input) === input ? 0 : 1;
                    } else {
                        mDec = 0;
                    }

                    settings.mDec = mDec;
                }

                tempEl.autoNumeric(settings);
                tempEl.autoNumeric('set', input);

                formatted = tempEl.text();

                if (suffix) {
                    formatted += suffix;
                }

                if (abbreviate && settings.pSign === 's' && aSign) {
                    formatted += aSign;
                }

                return formatted;
            };
        }]);
}(jQuery));
<commit_msg>log el val<commit_after>/*jslint browser: true, plusplus: true */
/*global angular, jQuery, console */
/** @module Autonumeric
@icon calculator
@summary The autonumeric component wraps up the autoNumeric jQuery plugin to format any type of number, including currency.
 @description The `bb-autonumeric` directive wraps up the autoNumeric jQuery plugin to format any type of number, including currency. You must use this directive in conjunction with the `ngModel` directive where the property bound to `ngModel` is the raw numeric value on your model.

 ### Dependencies ###

 - **[autoNumeric](http://www.decorplanit.com/plugin/) (1.9.27 or higher)** Used to format money values

---

### Autonumeric Settings ###

 - `bb-autonumeric` This  can optionally be assigned the name of a property from the `bbAutonumericConfig` object.  If none is specified, it defaults to `number`.
 - `bb-autonumeric-settings` This can be assigned a value that represents a settings object that can be passed to autoNumeric.  These options will override any default options specified in the `bb-autonumeric` attribute.  A complete list of options is available [here](http://www.decorplanit.com/plugin/).

### Autonumeric Filter ###

In addition to the directive, there is also a filter that can be used to format numbers.  The filter has the added feature of optionally abbreviating a number according to Sky patterns.  For instance,
numbers over 10,000 will be displayed as 10k, over 1,000,000 as 1m, and 1,000,000,000 as 1b.  The filter takes three arguments:

 - `input` The value to format.
 - `configType` The name of the configuration (`number` or `money`) to apply to the value.
 - `abbreviate` A Boolean value indicating whether to abbreviate large numbers.
 */
(function ($) {
    'use strict';

    function getBaseSettings(bbAutoNumericConfig, configType) {
        var baseSettings,
            configSettings;

        baseSettings = angular.extend(
            {},
            $.fn.autoNumeric.defaults,
            bbAutoNumericConfig.number
        );

        if (configType) {
            configSettings = bbAutoNumericConfig[configType];
        }

        if (configSettings) {
            angular.extend(baseSettings, configSettings);
        }

        return baseSettings;
    }

    angular.module('sky.autonumeric', ['sky.resources', 'sky.window'])
        .constant('bbAutonumericConfig', {
            number: {
                aSep: ',',
                dGroup: 3,
                aDec: '.',
                pSign: 'p',
                mDec: 2
            },
            money: {
                aSign: '$'
            }
        })
        .directive('bbAutonumeric', ['$timeout', 'bbAutonumericConfig', 'bbWindow', function ($timeout, bbAutoNumericConfig, bbWindow) {
            return {
                require: 'ngModel',
                restrict: 'A',
                link: function ($scope, el, attrs, ngModel) {
                    var customSettings = {},
                        isIosUserAgent = bbWindow.isIosUserAgent();

                    function applySettings() {
                        el.autoNumeric('update', angular.extend({}, getBaseSettings(bbAutoNumericConfig, attrs.bbAutonumeric), customSettings));
                    }

                    function applyCssSettings(el) {
                        if (attrs.bbAutonumeric) {
                            el.addClass('bb-autonumeric-' + attrs.bbAutonumeric);
                        }
                    }

                    function autonumericChange() {
                        return $scope.$apply(function () {

                            var value = parseFloat(el.autoNumeric('get'));
                            console.log('in autonumeric change');

                            if (isNaN(value)) {
                                value = null;
                            }

                            console.log('end autonumeric change');
                            return ngModel.$setViewValue(value);
                        });
                    }

                    if (attrs.bbAutonumericSettings) {
                        $scope.$watch(attrs.bbAutonumericSettings, function (newValue) {
                            customSettings = newValue || {};
                            applySettings();
                        }, true);
                    }

                    el.autoNumeric(getBaseSettings(bbAutoNumericConfig, attrs.bbAutonumeric));
                    applyCssSettings(el);

                    // If a valid number, update the autoNumeric value.
                    // Also handles the model being updated, but being in correct (usually a paste).
                    // In that case, updates the model to what the autoNumeric plugin's value.
                    $scope.$watch(attrs.ngModel, function (newValue, oldValue) {
                        var getValue,
                            selectionStart;
                        if (newValue !== undefined && newValue !== null && !isNaN(newValue)) {

                            if (parseFloat(newValue) !== parseFloat(oldValue)) {

                                selectionStart = el[0].selectionStart;
                            }

                            el.autoNumeric('set', newValue);
                            getValue = el.autoNumeric('get');
                            if (parseFloat(getValue) !== parseFloat(newValue)) {
                                $timeout(autonumericChange);
                            } else if (el[0] && angular.isFunction(el[0].setSelectionRange) && angular.isDefined(selectionStart)) {
                                $timeout(function () {
                                    console.log('before set selection', selectionStart);
                                    console.log('function defined', angular.isFunction(el[0].setSelectionRange));
                                    console.log('el val', el.val());
                                    el[0].setSelectionRange(selectionStart, selectionStart);
                                    console.log('after set selection');
                                });
                            }
                        } else if (newValue === null) {
                            el.val(null);

                        }
                    });

                    el.on('keydown', function (event) {
                        if (event.which === 13) {
                            autonumericChange();
                        }
                    });

                    el.on('change paste onpaste', function () {
                        autonumericChange();

                    });

                    // When focusing in textbox, select all.  This is to workaround not having placeholder text for autonumeric.
                    /*
                        istanbul ignore next: the test for this code isn't passing on IE 10 on BrowserStack in automated mode.
                        This isn't mission-critical so I'm just ignoring it for now.
                    */
                    el.on('focusin.bbAutonumeric', function () {
                        $timeout(function () {
                            // Check to ensure the field still has focus once the $timeout callback is executed.
                            // https://github.com/blackbaud/skyux/issues/64
                            if (el.is(':focus')) {
                                if (!isIosUserAgent) {
                                    el.select();
                                } else {
                                    //use setSelectionRange instead of select because select in a timeout does not work with iOS
                                    el[0].setSelectionRange(0, 9999);
                                }
                            }
                        });
                    });

                }
            };
        }])
        .filter('bbAutonumeric', ['bbAutonumericConfig', 'bbResources', function (bbAutonumericConfig, bbResources) {
            return function (input, configType, abbreviate) {
                var aSign,
                    dividend,
                    mDec,
                    formatted,
                    settings,
                    suffix,
                    tempEl;

                if (input === null || angular.isUndefined(input)) {
                    return '';
                }

                if (isNaN(input)) {
                    return input;
                }

                tempEl = $('');

                settings = getBaseSettings(bbAutonumericConfig, configType);

                if (abbreviate) {
                    if (settings.pSign === 's') {
                        // The suffix needs to go between the number and the currency symbol, so the currency
                        // symbol has to be left off and appended after the number is formatted.
                        aSign = settings.aSign;
                        settings.aSign = '';
                    }

                    input = Math.round(input);

                    if (input >= 1000000000) {
                        dividend = 100000000;
                        suffix = bbResources.autonumeric_abbr_billions;
                    } else if (input >= 1000000) {
                        dividend = 100000;
                        suffix = bbResources.autonumeric_abbr_millions;
                    } else if (input >= 10000) {
                        dividend = 100;
                        suffix = bbResources.autonumeric_abbr_thousands;
                    }

                    if (suffix) {
                        input = Math.floor(input / dividend) / 10;
                        mDec = Math.floor(input) === input ? 0 : 1;
                    } else {
                        mDec = 0;
                    }

                    settings.mDec = mDec;
                }

                tempEl.autoNumeric(settings);
                tempEl.autoNumeric('set', input);

                formatted = tempEl.text();

                if (suffix) {
                    formatted += suffix;
                }

                if (abbreviate && settings.pSign === 's' && aSign) {
                    formatted += aSign;
                }

                return formatted;
            };
        }]);
}(jQuery));
<|endoftext|>


<commit_before>"use strict";

var is = require("torf");

/**
 *	It is possible to inject any database
 *	in the controller. Just pass it as an
 *	argument when invoking the handler function.
 *	If no database is injected the handler
 *	will default to:
 *
 *		require("rubberbands")("clerk", "db");
 */

module.exports = function (database) {

	var db = database || require("rubberbands")("clerk", "members");

	return {
		search: function search (req, res) {

			var key   = is.ok(req.query) ? Object.keys(req.query)[0] : "*";
			var value = is.ok(req.query) ? req.query[key]            : "*";

			db.search(key, value, function (response) {

				if (response.hits.total > 0) {
					return res(response.hits.hits);
				} else {
					return res({ statusCode: 404, status: "missing", message: "invalid member"}).code(404);
				}
			});
		},
		create: function create (req, res) {

		  db.create(req.payload, function (response) {

		    return res(response);
		  });
		},
		read: function read (req, res) {

		  db.read(req.params.id, function (response) {

		    if (!response.found){
		      return res({ statusCode: 404, status: "missing", message: "invalid member"}).code(404);
		    }else{
		      return res(response);
		    }
		  });
		},
		update: function update (req, res) {

		  db.read(req.params.id, function (response) {

		    if (!response.found) {
		      return res({ statusCode: 404, status: "missing", message: "invalid member"}).code(404);
		    }
		    db.update(req.params.id, req.payload, function (response) {

		      return res(response);
		    });
		  });
		}
	};
};<commit_msg>reorganize handler memeber<commit_after>"use strict";

var is = require("torf");

/**
 *	It is possible to inject any database
 *	in the controller. Just pass it as an
 *	argument when invoking the handler function.
 *	If no database is injected the handler
 *	will default to:
 *
 *		require("rubberbands")("clerk", "db");
 */

module.exports = function (database) {

	var db = database || require("rubberbands")("clerk", "members");

	return {
		search: function search (req, res) {

			var key   = is.ok(req.query) ? Object.keys(req.query)[0] : "*";
			var value = is.ok(req.query) ? req.query[key]            : "*";

			db.search(key, value, function (response) {

				if (response.hits.total > 0) {
					return res(response.hits.hits);
				} else {
					return res({ statusCode: 404, status: "missing", message: "invalid member"}).code(404);
				}
			});
		},
		create: function create (req, res) {

			db.create(req.payload, function (response) {

				return res(response);
			});
		},
		read: function read (req, res) {

			db.read(req.params.id, function (response) {

				if (!response.found) {
					return res({ statusCode: 404, status: "missing", message: "invalid member"}).code(404);
				}else{
					return res(response);
				}
			});
		},
		update: function update (req, res) {

			db.read(req.params.id, function (response) {

				if (!response.found) {
					return res({ statusCode: 404, status: "missing", message: "invalid member"}).code(404);
				}
				db.update(req.params.id, req.payload, function (response) {

					return res(response);
				});
			});
		}
	};
};<|endoftext|>


<commit_before>const HELP_MESSAGE = "```\
        Niles Usage\n\
---------------------------\n\
!display             -  Display your calendar\n\
!update / !sync      -  Update the Calendar\n\
!create / !scrim     -  Create events using GCal's default interpreter - works best like !scrim xeno June 5 8pm - 9pm\n\
!delete              -  Delete an event using the form !delete <event_title>, ONLY works when the title matches the one in Google Calendar\n\
!clean / !purge      -  Deletes messages in current channel, !clean <number>\n\
!stats / !info       -  Display list of statistics and information about the Niles bot\n\
!invite              -  Get the invite link for Niles to join your server!\n\
!setup               -  Get details on how to setup Niles\n\
!id                  -  Set the Google calendar ID for the guild\n\
!tz                  -  Set the timezone for the guild\n\
!prefix              -  View or change the prefix for Niles\n\
!displayoptions      -  Change formatting of the calendar display\n\
!admin               -  Restrict the usage of Niles to a specific role\n\
!help                -  Display this message\n\
```\
Visit https://nilesbot.com for more info.";

const NO_CALENDAR_MESSAGE = "I can't seem to find your calendar! This is usually because you haven't invited Niles to access your calendar, run `!setup` to make sure you followed Step 1.\n\
You should also check that you have entered the correct calendar id using `!id`.\n\
\nIf you are still getting this error join the Discord support server here: https://discord.gg/jNyntBn";

const SETUP_HELP = "```\
        Niles Usage - SETUP MODE\n\
---------------------------\n\
NOTE: ALL COMMANDS BECOME AVAILABLE AFTER SETUP IS COMPLETE\n\
!setup               -  Get details on how to setup Niles for use.\n\
!prefix              -  View or change the prefix for Niles\n\
!id                  -  Set the Google calendar ID for the guild\n\
!tz                  -  Set the timezone for the guild\n\
!adminrole           -  Restrict the usage of Niles to a specific role\n\
!help                -  Display this message\n\
```\n\
Visit https://nilesbot.com/start for more info.";

const SETUP_MESSAGE = "\
Hi! Lets get me setup for use in this Discord. The steps are outlined below, but for a detailed setup guide, visit https://nilesbot.com/start \n\
\n1. Invite `niles-291@niles-169605.iam.gserviceaccount.com` to \'Make changes to events\' under the Permission Settings on the Google Calendar you want to use with Niles\n\
2. Enter the Calendar ID of the calendar to Discord using the `!id` command, i.e. `!id 123abc@123abc.com`\n\
3. Enter the timezone you want to use in Discord with the `!tz` command, i.e. i.e. `!tz America/New_York` or `!tz UTC+4` or `!tz EST` No spaces in formatting.\n\
\n Niles should now be able to sync with your Google calendar and interact with on you on Discord, try `!display` to get started!";

const RESTRICT_ROLE_MESSAGE = "\
You can restrict who can control Niles and the associated Google Calendar with roles. \n\
Niles will only allow one role to be used, and it must have a unique name. \n\
The person assigning the restriction must have the role being assigned. \n\
i.e. Create a role called *Scheduler*, and then tell Niles to only allow people with that role using `!admin Scheduler` (case sensitive)\n\
**Warning - Experimental Feature - Please go to the Niles discord server and post if you have any issues with this feature**";

const DISPLAYOPTIONS_USAGE =
`**displayoptions USAGE**\`\`\`
    COMMAND                      PARAMS        EFFECT
    !displayoptions help         (0|1)         hide/show help
    !displayoptions pin          (0|1)         pin calendar message
    !displayoptions format       (12|24)       12h or 24h clock display
    !displayoptions tzdisplay    (0|1)         hide/show timezone
    !displayoptions emptydays    (0|1)         hide/show empty days
    !displayoptions showpast     (0|1)         hide/show today's past events
    !displayoptions trim         (n)           trim event names to n characters (0 = off)
    !displayoptions days         (n)           number of days to display (max 90)
    !displayoptions style        (code|embed)  use old or new event display style (see nilesbot.com/customisation)
    !displayoptions inline       (0|1)         makes embed display inline (see nilesbot.com/customisation)
    !displayoptions description  (0|1)         hide/show event description (only compatible with embed)
    \`\`\`
    `;

module.exports = {
  HELP_MESSAGE,
  NO_CALENDAR_MESSAGE,
  SETUP_MESSAGE,
  SETUP_HELP,
  RESTRICT_ROLE_MESSAGE,
  DISPLAYOPTIONS_USAGE
};
<commit_msg>updated help command<commit_after>const HELP_MESSAGE = "```\
        Niles Usage\n\
---------------------------\n\
!display             -  Display your calendar\n\
!update / !sync      -  Update the Calendar\n\
!create / !scrim     -  Create events using GCal's default interpreter - works best like !scrim xeno June 5 8pm - 9pm\n\
!delete              -  Delete an event using the form !delete <event_title>, ONLY works when the title matches the one in Google Calendar\n\
!clean / !purge      -  Deletes messages in current channel, !clean <number>\n\
!stats / !info       -  Display list of statistics and information about the Niles bot\n\
!invite              -  Get the invite link for Niles to join your server!\n\
!setup               -  Get details on how to setup Niles\n\
!id                  -  Set the Google calendar ID for the guild\n\
!tz                  -  Set the timezone for the guild\n\
!prefix              -  View or change the prefix for Niles\n\
!displayoptions      -  Change formatting of the calendar display\n\
!admin               -  Restrict the usage of Niles to a specific role\n\
!help                -  Display this message\n\
!next                -  Displays the next upcoming event\n\
!validate            -  Checks for any errors with the timezone, calendar ID or calendar fetching\n\
```\
Visit https://nilesbot.com for more info.";

const NO_CALENDAR_MESSAGE = "I can't seem to find your calendar! This is usually because you haven't invited Niles to access your calendar, run `!setup` to make sure you followed Step 1.\n\
You should also check that you have entered the correct calendar id using `!id`.\n\
\nIf you are still getting this error join the Discord support server here: https://discord.gg/jNyntBn";

const SETUP_HELP = "```\
        Niles Usage - SETUP MODE\n\
---------------------------\n\
NOTE: ALL COMMANDS BECOME AVAILABLE AFTER SETUP IS COMPLETE\n\
!setup               -  Get details on how to setup Niles for use.\n\
!prefix              -  View or change the prefix for Niles\n\
!id                  -  Set the Google calendar ID for the guild\n\
!tz                  -  Set the timezone for the guild\n\
!adminrole           -  Restrict the usage of Niles to a specific role\n\
!help                -  Display this message\n\
```\n\
Visit https://nilesbot.com/start for more info.";

const SETUP_MESSAGE = "\
Hi! Lets get me setup for use in this Discord. The steps are outlined below, but for a detailed setup guide, visit https://nilesbot.com/start \n\
\n1. Invite `niles-291@niles-169605.iam.gserviceaccount.com` to \'Make changes to events\' under the Permission Settings on the Google Calendar you want to use with Niles\n\
2. Enter the Calendar ID of the calendar to Discord using the `!id` command, i.e. `!id 123abc@123abc.com`\n\
3. Enter the timezone you want to use in Discord with the `!tz` command, i.e. i.e. `!tz America/New_York` or `!tz UTC+4` or `!tz EST` No spaces in formatting.\n\
\n Niles should now be able to sync with your Google calendar and interact with on you on Discord, try `!display` to get started!";

const RESTRICT_ROLE_MESSAGE = "\
You can restrict who can control Niles and the associated Google Calendar with roles. \n\
Niles will only allow one role to be used, and it must have a unique name. \n\
The person assigning the restriction must have the role being assigned. \n\
i.e. Create a role called *Scheduler*, and then tell Niles to only allow people with that role using `!admin Scheduler` (case sensitive)\n\
**Warning - Experimental Feature - Please go to the Niles discord server and post if you have any issues with this feature**";

const DISPLAYOPTIONS_USAGE =
`**displayoptions USAGE**\`\`\`
    COMMAND                      PARAMS        EFFECT
    !displayoptions help         (0|1)         hide/show help
    !displayoptions pin          (0|1)         pin calendar message
    !displayoptions format       (12|24)       12h or 24h clock display
    !displayoptions tzdisplay    (0|1)         hide/show timezone
    !displayoptions emptydays    (0|1)         hide/show empty days
    !displayoptions showpast     (0|1)         hide/show today's past events
    !displayoptions trim         (n)           trim event names to n characters (0 = off)
    !displayoptions days         (n)           number of days to display (max 90)
    !displayoptions style        (code|embed)  use old or new event display style (see nilesbot.com/customisation)
    !displayoptions inline       (0|1)         makes embed display inline (see nilesbot.com/customisation)
    !displayoptions description  (0|1)         hide/show event description (only compatible with embed)
    \`\`\`
    `;

module.exports = {
  HELP_MESSAGE,
  NO_CALENDAR_MESSAGE,
  SETUP_MESSAGE,
  SETUP_HELP,
  RESTRICT_ROLE_MESSAGE,
  DISPLAYOPTIONS_USAGE
};
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

"use strict";

const { click, addNewLayer, waitForEvents, removeLayer } = require("./common.js");
const { closeTab } = require("sdk/tabs/utils");

/**
 * xxxHonza TODO docs
 */
exports["test Layer Invert"] = function(assert, done) {
  addNewLayer().then(config => {
    let popup = config.popup;
    let layer = config.layer;

    // Click on the 'Invert' checkbox.
    click(popup, "#pixel-perfect-invert");

    // Wait till the layer is modified on the backend.
    waitForEvents(popup, ["layer-modified"]).then(result => {
      console.log("!!! waitForEvents (invert) " + layer.id, result);

      // Get layer info from the backend.
      popup.front.getLayerInfo(layer.id).then(response => {
        console.log("!!! invert", response)
        assert.equal(response.content.invert, "true",
          "The layer must be inverted");

        // Clean up
        removeLayer(popup, layer.id).then(() => {
          config.popup.destroy().then(() => {
            closeTab(config.tab);
            done();
          });
        });
      })
    });
  });
};

require("sdk/test").run(exports);
<commit_msg>Remove helper logs<commit_after>/* See license.txt for terms of usage */

"use strict";

const { click, addNewLayer, waitForEvents, removeLayer } = require("./common.js");
const { closeTab } = require("sdk/tabs/utils");

/**
 * xxxHonza TODO docs
 */
exports["test Layer Invert"] = function(assert, done) {
  addNewLayer().then(config => {
    let popup = config.popup;
    let layer = config.layer;

    // Click on the 'Invert' checkbox.
    click(popup, "#pixel-perfect-invert");

    // Wait till the layer is modified on the backend.
    waitForEvents(popup, ["layer-modified"]).then(result => {
      // Get layer info from the backend.
      popup.front.getLayerInfo(layer.id).then(response => {
        assert.equal(response.content.invert, "true",
          "The layer must be inverted");

        // Clean up
        removeLayer(popup, layer.id).then(() => {
          config.popup.destroy().then(() => {
            closeTab(config.tab);
            done();
          });
        });
      })
    });
  });
};

require("sdk/test").run(exports);
<|endoftext|>


<commit_before>const mongoose = require('mongoose');
const chai = require('chai');
const chaiHttp = require('chai-http');

const should = chai.should();

const {app, runServer, closeServer} = require('../server');
const {DATABASE_URL, PORT} = require('../config');

chai.use(chaiHttp);

describe('index', function() {
  // before the tests runs statr the server once
  before(function() {
    return runServer(DATABASE_URL);
  });

  // after all tests have been completed close out the server
after(function() {
  return closeServer();
});

  it('should return homepage', function() {
    return chai.request(app)
      .get('/')
      .then(function(res) {
        res.should.have.status(200);
      });
  });

});
<commit_msg>fixed route for database<commit_after>const mongoose = require('mongoose');
const chai = require('chai');
const chaiHttp = require('chai-http');

const should = chai.should();

const {app, runServer, closeServer} = require('../server');
const {DATABASE_URL, PORT} = require('../config/database');

chai.use(chaiHttp);

describe('index', function() {
  // before the tests runs statr the server once
  before(function() {
    return runServer(DATABASE_URL);
  });

  // after all tests have been completed close out the server
after(function() {
  return closeServer();
});

  it('should return homepage', function() {
    return chai.request(app)
      .get('/')
      .then(function(res) {
        res.should.have.status(200);
      });
  });

});
<|endoftext|>


<commit_before><commit_msg>erased the creeps' path memory after switching modes<commit_after><|endoftext|>


<commit_before>/**
 * @copyright
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Cosmic Dynamo LLC
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * @module $<class>$
 */
define([
    "dojo/_base/declare"
], function (declare) {
    /**
     * @class $<class>$
     */
    return declare([], {

    });
});<commit_msg>Adding more tests.  Making them pass<commit_after><|endoftext|>


<commit_before>/*
 Created by ℭrystaℒ on 7/10/2017.
 */
const Discord = require('discord.js');
const bot = new Discord.Client();
const fs = require("fs");
require("./functions/functions.js")(bot);
console.log(`Setting max listeners to:] :${bot.config.maxL}`);
bot.setMaxListeners(bot.config.maxL);
console.log(`Max listeners :${bot.getMaxListeners()}`);
fs.readdir("./events/", (err, files) => {
console.log(`Logging in Neko
  \\    /\\
   )  ( \')
   (  /  )
    \\(__)|
Adding The following events`);
    console.log(`${files.map(g => `${g.split(".")[0]}`).join(", ")}`);
    if (err) return console.error(err);
    files.forEach(file => {
        let eventFunction = require(`./events/${file}`);
        let eventName = file.split(".")[0];
        bot.on(eventName, (...args) => eventFunction.run(bot, ...args));
    });
    console.log(`Added ${files.length} events.`);
    fs.readdir("./commands/", (err, files) => {
        console.log(`Adding The following Commands\n${files.map(g => `${g.split(".")[0]}`).join(", ")}\nAdded ${files.length} Commands.
Ram used: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB
Version info
Node:${process.version} D.js: ${Discord.version} Neko: ${bot.config.nekover}`);
    });
});
bot.login(bot.config.token).catch(e => console.warn('wew tf happened here ' + e));

<commit_msg>Update neko.js<commit_after>/*
 Created by ℭrystaℒ on 7/10/2017 <3
 */
const Discord = require('discord.js');
const bot = new Discord.Client();
const fs = require("fs");
require("./functions/functions.js")(bot);
console.log(`Setting max listeners to:] :${bot.config.maxL}`);
bot.setMaxListeners(bot.config.maxL);
console.log(`Max listeners :${bot.getMaxListeners()}`);
fs.readdir("./events/", (err, files) => {
console.log(`Logging in Neko
  \\    /\\
   )  ( \')
   (  /  )
    \\(__)|
Adding The following events`);
    console.log(`${files.map(g => `${g.split(".")[0]}`).join(", ")}`);
    if (err) return console.error(err);
    files.forEach(file => {
        let eventFunction = require(`./events/${file}`);
        let eventName = file.split(".")[0];
        bot.on(eventName, (...args) => eventFunction.run(bot, ...args));
    });
    console.log(`Added ${files.length} events.`);
    fs.readdir("./commands/", (err, files) => {
        console.log(`Adding The following Commands\n${files.map(g => `${g.split(".")[0]}`).join(", ")}\nAdded ${files.length} Commands.
Ram used: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB
Version info
Node:${process.version} D.js: ${Discord.version} Neko: ${bot.config.nekover}`);
    });
});
bot.login(bot.config.token).catch(e => console.warn('wew tf happened here ' + e));

<|endoftext|>