<commit_before>import { addListener } from '@ember/object/events';
import { registerWarnHandler } from '@ember/debug';
import { settled } from '@ember/test-helpers';
import { isHTMLSafe } from '@ember/string';
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from '@ember/object';
import td from 'testdouble';

const LOCALE = 'en-us';

module('service:init initialization', function(hooks) {
  setupTest(hooks);

  test('it calls `setLocale` on init', function(assert) {
    const setLocale = td.func();
    const Intl = this.owner.factoryFor('service:intl');

    Intl.create({ setLocale });
    assert.verify(setLocale([LOCALE]));
  });
});

module('service:intl', function(hooks) {
  setupTest(hooks);

  hooks.beforeEach(function() {
    this.intl = this.owner.lookup('service:intl');
    this.intl.setLocale([LOCALE]);
  });

  test('can access formatMessage without a locale set', function(assert) {
    this.intl.t('does.not.exist');
    assert.ok(true, 'Exception was not raised');
  });

  test('should return a number if the translation is a number', function(assert) {
    this.intl.addTranslations(LOCALE, {
      a_number: 2
    });

    assert.equal(this.intl.t('a_number'), 2);
  });

  test('`t` should cascade translation lookup', function(assert) {
    this.intl.addTranslations(LOCALE, {
      should_exist: 'I do exist!',
      should_also_exist: 'I do also exist!'
    });

    assert.equal(
      this.intl.t('does.not.exist', {
        default: ['also.does.not.exist', 'should_exist', 'should_also_exist']
      }),
      'I do exist!'
    );
  });

  test('should deepMerge addTranslations', function(assert) {
    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: {
            a: 'a'
          }
        }
      }
    });

    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: {
            b: 'b'
          },
          c: {
            d: {
              e: {}
            }
          }
        }
      }
    });

    assert.equal(this.intl.t('foo.bar.baz.a'), 'a');
    assert.equal(this.intl.t('foo.bar.baz.b'), 'b');
  });

  test('should overwrite translations', function(assert) {
    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: 'baz'
        }
      }
    });

    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: 'baz!'
        }
      }
    });

    assert.equal(this.intl.t('foo.bar.baz'), 'baz!');
  });

  test('`t` should display last missing translation key when using default', function(assert) {
    assert.equal(
      this.intl.t('x', {
        default: ['y', 'z']
      }),
      `Missing translation "z" for locale "en-us"`
    );
  });

  test('`t` should pass `key`, `locales`, `options` through to `missing-message` util', function(assert) {
    assert.expect(3);

    this.owner.register('util:intl/missing-message', (key, locales, options) => {
      assert.strictEqual(key, 'should_also_not_exist');
      assert.deepEqual(locales, [LOCALE]);
      assert.deepEqual(options, {
        default: ['also.does.not.exist', 'should_also_not_exist'],
        resilient: false,
        someVariable: 'hello'
      });

      return '';
    });

    this.intl.t('does.not.exist', {
      default: ['also.does.not.exist', 'should_also_not_exist'],
      someVariable: 'hello'
    });
  });

  test('triggers notifyPropertyChange only when locale changes', function(assert) {
    let count = 0;

    function increment() {
      ++count;
    }

    this.intl.addObserver('locale', this.intl, increment);
    this.intl.setLocale(['es']);
    this.intl.setLocale(['fr']);
    assert.equal(count, 2);
    assert.equal(this.intl.get('locale'), 'fr');
    this.intl.removeObserver('locale', this.intl, increment);
  });

  test('waits for translations to load', async function(assert) {
    assert.expect(1);
    await settled();
    assert.equal(this.intl.t('product.title', { locale: 'en-us' }), 'Hello');
  });

  test('it does not mutate t options hash', function(assert) {
    this.intl.setLocale(LOCALE);
    const obj = { bar: 'bar' };
    this.intl.t('foo', obj);
    assert.ok(typeof obj.locale === 'undefined');
  });

  test('`t` can be passed a null options hash', function(assert) {
    this.intl.setLocale(LOCALE);
    this.intl.t('foo', undefined);
    assert.ok(true, 'Exception was not raised');
  });

  test('`t` can be passed a no options argument and no warning should be emitted', async function(assert) {
    this.intl.setLocale(LOCALE);

    let invokedWarn = false;
    registerWarnHandler(function() {
      invokedWarn = true;
    });

    this.intl.addTranslations(LOCALE, { foo: 'FOO' });
    this.intl.t('foo');
    assert.ok(!invokedWarn, 'Warning was not raised');
  });

  test('translations that are empty strings are valid', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, { empty_string: '' });
    assert.equal(this.intl.t('empty_string'), '');
  });

  test('translations keys can contain periods within the key', async function(assert) {
    assert.expect(2);

    this.intl.addTranslations(LOCALE, {
      a: {
        b: {
          c: {
            'd.d': {
              'e.e': 'Periods within a key work are now valid.'
            }
          }
        }
      },
      'b.b': 'Root key with double period.'
    });

    assert.equal(this.intl.t('a.b.c.d.d.e.e'), 'Periods within a key work are now valid.');
    assert.equal(this.intl.t('b.b'), 'Root key with double period.');
  });

  test('should return safestring when htmlSafe attribute passed to `t`', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, {
      html_safe_translation: 'Hello <em>Jason</em> 42,000'
    });

    const out = this.intl.t('html_safe_translation', {
      htmlSafe: true,
      name: 'Jason',
      count: 42000
    });

    assert.ok(isHTMLSafe(out));
  });

  test('should return regular string when htmlSafe is falsey', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, {
      html_safe_translation: 'Hello <em>Jason</em> 42,000'
    });

    const out = this.intl.t('html_safe_translation', {
      htmlSafe: false,
      name: 'Jason',
      count: 42000
    });

    assert.ok(!isHTMLSafe(out));
  });

  test('exists returns true when key found', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, { hello: 'world' });
    assert.equal(this.intl.exists('hello'), true);
  });

  test('exists returns false when key not found', function(assert) {
    assert.expect(1);
    assert.equal(this.intl.exists('bar'), false);
  });

  test('changing the locale emits the `localeChanged` event and the new locale is available', async function(assert) {
    assert.expect(1);

    const newLocale = ['de', 'en-us'];

    addListener(this.intl, 'localeChanged', () => {
      assert.deepEqual(get(this.intl, 'locale'), newLocale);
    });

    this.intl.setLocale(newLocale);
  });

  test('updates document `lang` attribute on locale change', async function(assert) {
    assert.expect(2);

    this.intl.setLocale(['de']);
    await settled();
    assert.equal(document.documentElement.getAttribute('lang'), 'de');

    this.intl.setLocale(['es', 'fr']);
    await settled();
    assert.equal(document.documentElement.getAttribute('lang'), 'es');
  });

  test('primaryLocale returns the first locale of the currently active locales', async function(assert) {
    assert.expect(2);

    assert.equal(this.intl.get('primaryLocale'), LOCALE);

    this.intl.setLocale(['de']);
    assert.equal(this.intl.get('primaryLocale'), 'de');
  });
});
<commit_msg>chore: lint changes for new ember eslint plugin rules (#1145)<commit_after>import { addListener } from '@ember/object/events';
import { registerWarnHandler } from '@ember/debug';
import { settled } from '@ember/test-helpers';
import { isHTMLSafe } from '@ember/string';
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { get } from '@ember/object';
import td from 'testdouble';

const LOCALE = 'en-us';

module('service:init initialization', function(hooks) {
  setupTest(hooks);

  test('it calls `setLocale` on init', function(assert) {
    const setLocale = td.func();
    const Intl = this.owner.factoryFor('service:intl');

    Intl.create({ setLocale });
    assert.verify(setLocale([LOCALE]));
  });
});

module('service:intl', function(hooks) {
  setupTest(hooks);

  hooks.beforeEach(function() {
    this.intl = this.owner.lookup('service:intl');
    this.intl.setLocale([LOCALE]);
  });

  test('can access formatMessage without a locale set', function(assert) {
    this.intl.t('does.not.exist');
    assert.ok(true, 'Exception was not raised');
  });

  test('should return a number if the translation is a number', function(assert) {
    this.intl.addTranslations(LOCALE, {
      a_number: 2
    });

    assert.equal(this.intl.t('a_number'), 2);
  });

  test('`t` should cascade translation lookup', function(assert) {
    this.intl.addTranslations(LOCALE, {
      should_exist: 'I do exist!',
      should_also_exist: 'I do also exist!'
    });

    assert.equal(
      this.intl.t('does.not.exist', {
        default: ['also.does.not.exist', 'should_exist', 'should_also_exist']
      }),
      'I do exist!'
    );
  });

  test('should deepMerge addTranslations', function(assert) {
    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: {
            a: 'a'
          }
        }
      }
    });

    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: {
            b: 'b'
          },
          c: {
            d: {
              e: {}
            }
          }
        }
      }
    });

    assert.equal(this.intl.t('foo.bar.baz.a'), 'a');
    assert.equal(this.intl.t('foo.bar.baz.b'), 'b');
  });

  test('should overwrite translations', function(assert) {
    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: 'baz'
        }
      }
    });

    this.intl.addTranslations(LOCALE, {
      foo: {
        bar: {
          baz: 'baz!'
        }
      }
    });

    assert.equal(this.intl.t('foo.bar.baz'), 'baz!');
  });

  test('`t` should display last missing translation key when using default', function(assert) {
    assert.equal(
      this.intl.t('x', {
        default: ['y', 'z']
      }),
      `Missing translation "z" for locale "en-us"`
    );
  });

  test('`t` should pass `key`, `locales`, `options` through to `missing-message` util', function(assert) {
    assert.expect(3);

    this.owner.register('util:intl/missing-message', (key, locales, options) => {
      assert.strictEqual(key, 'should_also_not_exist');
      assert.deepEqual(locales, [LOCALE]);
      assert.deepEqual(options, {
        default: ['also.does.not.exist', 'should_also_not_exist'],
        resilient: false,
        someVariable: 'hello'
      });

      return '';
    });

    this.intl.t('does.not.exist', {
      default: ['also.does.not.exist', 'should_also_not_exist'],
      someVariable: 'hello'
    });
  });

  test('triggers notifyPropertyChange only when locale changes', function(assert) {
    let count = 0;

    function increment() {
      ++count;
    }

    // eslint-disable-next-line ember/no-observers
    this.intl.addObserver('locale', this.intl, increment);
    this.intl.setLocale(['es']);
    this.intl.setLocale(['fr']);
    assert.equal(count, 2);
    assert.equal(this.intl.get('locale'), 'fr');
    this.intl.removeObserver('locale', this.intl, increment);
  });

  test('waits for translations to load', async function(assert) {
    assert.expect(1);
    await settled();
    assert.equal(this.intl.t('product.title', { locale: 'en-us' }), 'Hello');
  });

  test('it does not mutate t options hash', function(assert) {
    this.intl.setLocale(LOCALE);
    const obj = { bar: 'bar' };
    this.intl.t('foo', obj);
    assert.ok(typeof obj.locale === 'undefined');
  });

  test('`t` can be passed a null options hash', function(assert) {
    this.intl.setLocale(LOCALE);
    this.intl.t('foo', undefined);
    assert.ok(true, 'Exception was not raised');
  });

  test('`t` can be passed a no options argument and no warning should be emitted', async function(assert) {
    this.intl.setLocale(LOCALE);

    let invokedWarn = false;
    registerWarnHandler(function() {
      invokedWarn = true;
    });

    this.intl.addTranslations(LOCALE, { foo: 'FOO' });
    this.intl.t('foo');
    assert.ok(!invokedWarn, 'Warning was not raised');
  });

  test('translations that are empty strings are valid', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, { empty_string: '' });
    assert.equal(this.intl.t('empty_string'), '');
  });

  test('translations keys can contain periods within the key', async function(assert) {
    assert.expect(2);

    this.intl.addTranslations(LOCALE, {
      a: {
        b: {
          c: {
            'd.d': {
              'e.e': 'Periods within a key work are now valid.'
            }
          }
        }
      },
      'b.b': 'Root key with double period.'
    });

    assert.equal(this.intl.t('a.b.c.d.d.e.e'), 'Periods within a key work are now valid.');
    assert.equal(this.intl.t('b.b'), 'Root key with double period.');
  });

  test('should return safestring when htmlSafe attribute passed to `t`', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, {
      html_safe_translation: 'Hello <em>Jason</em> 42,000'
    });

    const out = this.intl.t('html_safe_translation', {
      htmlSafe: true,
      name: 'Jason',
      count: 42000
    });

    assert.ok(isHTMLSafe(out));
  });

  test('should return regular string when htmlSafe is falsey', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, {
      html_safe_translation: 'Hello <em>Jason</em> 42,000'
    });

    const out = this.intl.t('html_safe_translation', {
      htmlSafe: false,
      name: 'Jason',
      count: 42000
    });

    assert.ok(!isHTMLSafe(out));
  });

  test('exists returns true when key found', async function(assert) {
    assert.expect(1);

    this.intl.addTranslations(LOCALE, { hello: 'world' });
    assert.equal(this.intl.exists('hello'), true);
  });

  test('exists returns false when key not found', function(assert) {
    assert.expect(1);
    assert.equal(this.intl.exists('bar'), false);
  });

  test('changing the locale emits the `localeChanged` event and the new locale is available', async function(assert) {
    assert.expect(1);

    const newLocale = ['de', 'en-us'];

    addListener(this.intl, 'localeChanged', () => {
      assert.deepEqual(get(this.intl, 'locale'), newLocale);
    });

    this.intl.setLocale(newLocale);
  });

  test('updates document `lang` attribute on locale change', async function(assert) {
    assert.expect(2);

    this.intl.setLocale(['de']);
    await settled();
    assert.equal(document.documentElement.getAttribute('lang'), 'de');

    this.intl.setLocale(['es', 'fr']);
    await settled();
    assert.equal(document.documentElement.getAttribute('lang'), 'es');
  });

  test('primaryLocale returns the first locale of the currently active locales', async function(assert) {
    assert.expect(2);

    assert.equal(this.intl.get('primaryLocale'), LOCALE);

    this.intl.setLocale(['de']);
    assert.equal(this.intl.get('primaryLocale'), 'de');
  });
});
<|endoftext|>


<commit_before>'use strict';

function draw_logic(){
    for(var vertex in vertices){
        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertex]['dy']
        );
        buffer.lineTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();

        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.lineTo(
          vertices[vertices[vertex]['parent']]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();
    }

    // Draw vertices_amount and lineWidth.
    buffer.fillText(
      vertices_amount,
      0,
      25
    );
    buffer.fillText(
      buffer.lineWidth,
      0,
      50
    );
}

function logic(){
    for(var vertex in vertices){
        vertices[vertex]['loop'] += vertices[vertex]['speed'];
        if(vertices[vertex]['loop'] > 359){
            vertices[vertex]['loop'] -= 360;

        }else if(vertices[vertex]['loop'] < 0){
            vertices[vertex]['loop'] += 360;
        }

        var rotation = vertices[vertex]['loop'] * degree;

        vertices[vertex]['dx'] =
          x
          + vertices[vertex]['x']
          + vertices[vertex]['radius']
          * Math.cos(rotation);
        vertices[vertex]['dy'] =
          y
          + vertices[vertex]['y']
          + vertices[vertex]['radius']
          * Math.sin(rotation);
    }
}

function randomize(){
    vertices.length = 0;

    var loop_counter = vertices_amount - 1;
    do{
        vertices.push({
          'dx': 0,
          'dy': 0,
          'loop': Math.floor(Math.random() * 50) + 50,
          'parent': vertices_amount - loop_counter,
          'radius': Math.random() * 20 + 5,
          'speed': Math.floor(Math.random() * 10) - 5 * 2,
          'x': Math.floor(Math.random() * 500) - 250,
          'y': Math.floor(Math.random() * 500) - 250,
        });
    }while(loop_counter--);

    vertices[vertices_amount - 1]['parent'] = 0;
}

function resize_logic(){
    buffer.fillStyle = '#fff';
    buffer.font = font;
    buffer.lineJoin = 'round';
    buffer.strokeStyle = '#fff';

    randomize();
}

var degree = Math.PI / 180;
var vertices = [];
var vertices_amount = 23;

window.onload = function(e){
    init_canvas();
    init_input(
      {
        27: {
          'todo': function(){
              buffer.lineWidth = 1;
              vertices_amount = 23;
              randomize();
          },
        },
        65: {
          'todo': function(){
              buffer.lineWidth = Math.max(
                buffer.lineWidth - 1,
                1
              );
          },
        },
        68: {
          'todo': function(){
              buffer.lineWidth += 1;
          },
        },
        83: {
          'todo': function(){
              vertices_amount = Math.max(
                vertices_amount - 1,
                2
              );
              randomize();
          },
        },
        87: {
          'todo': function(){
              vertices_amount += 1;
              randomize();
          },
        },
      },
      {
        'mousedown': {
          'todo': randomize,
        },
      }
    );
};
<commit_msg>switched to default font<commit_after>'use strict';

function draw_logic(){
    for(var vertex in vertices){
        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertex]['dy']
        );
        buffer.lineTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();

        buffer.beginPath();
        buffer.moveTo(
          vertices[vertex]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.lineTo(
          vertices[vertices[vertex]['parent']]['dx'],
          vertices[vertices[vertex]['parent']]['dy']
        );
        buffer.closePath();
        buffer.stroke();
    }

    // Draw vertices_amount and lineWidth.
    buffer.fillText(
      vertices_amount,
      0,
      25
    );
    buffer.fillText(
      buffer.lineWidth,
      0,
      50
    );
}

function logic(){
    for(var vertex in vertices){
        vertices[vertex]['loop'] += vertices[vertex]['speed'];
        if(vertices[vertex]['loop'] > 359){
            vertices[vertex]['loop'] -= 360;

        }else if(vertices[vertex]['loop'] < 0){
            vertices[vertex]['loop'] += 360;
        }

        var rotation = vertices[vertex]['loop'] * degree;

        vertices[vertex]['dx'] =
          x
          + vertices[vertex]['x']
          + vertices[vertex]['radius']
          * Math.cos(rotation);
        vertices[vertex]['dy'] =
          y
          + vertices[vertex]['y']
          + vertices[vertex]['radius']
          * Math.sin(rotation);
    }
}

function randomize(){
    vertices.length = 0;

    var loop_counter = vertices_amount - 1;
    do{
        vertices.push({
          'dx': 0,
          'dy': 0,
          'loop': Math.floor(Math.random() * 50) + 50,
          'parent': vertices_amount - loop_counter,
          'radius': Math.random() * 20 + 5,
          'speed': Math.floor(Math.random() * 10) - 5 * 2,
          'x': Math.floor(Math.random() * 500) - 250,
          'y': Math.floor(Math.random() * 500) - 250,
        });
    }while(loop_counter--);

    vertices[vertices_amount - 1]['parent'] = 0;
}

function resize_logic(){
    buffer.fillStyle = '#fff';
    buffer.lineJoin = 'round';
    buffer.strokeStyle = '#fff';

    randomize();
}

var degree = Math.PI / 180;
var vertices = [];
var vertices_amount = 23;

window.onload = function(e){
    init_canvas();
    init_input(
      {
        27: {
          'todo': function(){
              buffer.lineWidth = 1;
              vertices_amount = 23;
              randomize();
          },
        },
        65: {
          'todo': function(){
              buffer.lineWidth = Math.max(
                buffer.lineWidth - 1,
                1
              );
          },
        },
        68: {
          'todo': function(){
              buffer.lineWidth += 1;
          },
        },
        83: {
          'todo': function(){
              vertices_amount = Math.max(
                vertices_amount - 1,
                2
              );
              randomize();
          },
        },
        87: {
          'todo': function(){
              vertices_amount += 1;
              randomize();
          },
        },
      },
      {
        'mousedown': {
          'todo': randomize,
        },
      }
    );
};
<|endoftext|>


<commit_before>'use strict'

var test = require('tap').test
var pino = require('../')
var sink = require('./helper').sink

test('can add a custom level via constructor', function (t) {
  t.plan(2)

  var log = pino({level: 'foo', levelVal: 35}, sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    cb()
  }))

  t.is(typeof log.foo, 'function')
  log.foo('bar')
})

test('can add a custom level to a prior instance', function (t) {
  t.plan(2)

  var log = pino(sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
  }))

  log.addLevel('foo2', 35)
  t.is(typeof log.foo2, 'function')
  log.foo2('bar')
})

test('custom levels encompass higher levels', function (t) {
  t.plan(1)

  var log = pino({level: 'foo', levelVal: 35}, sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    cb()
  }))

  log.warn('bar')
})

test('after the fact add level does not include lower levels', function (t) {
  t.plan(1)

  var log = pino(sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    cb()
  }))

  log.addLevel('foo', 35)
  log.level = 'foo'
  log.info('nope')
  log.foo('bar')
})

test('children can be set to custom level', function (t) {
  t.plan(2)

  var parent = pino({level: 'foo', levelVal: 35}, sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    t.is(chunk.child, 'yes')
    cb()
  }))
  var child = parent.child({child: 'yes'})
  child.foo('bar')
})

test('rejects already known labels', function (t) {
  t.plan(1)
  var log = pino({level: 'info', levelVal: 900})
  t.is(log.levelVal, 30)
})

test('reject already known values', function (t) {
  t.plan(1)
  try {
    pino({level: 'foo', levelVal: 30})
  } catch (e) {
    t.is(e.message.indexOf('level value') > -1, true)
  }
})
<commit_msg>Add test to show why 'can add a custom level to a prior instance' worked<commit_after>'use strict'

var test = require('tap').test
var pino = require('../')
var sink = require('./helper').sink

test('can add a custom level via constructor', function (t) {
  t.plan(2)

  var log = pino({level: 'foo', levelVal: 35}, sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    cb()
  }))

  t.is(typeof log.foo, 'function')
  log.foo('bar')
})

test('can add a custom level to a prior instance', function (t) {
  t.plan(2)

  var log = pino(sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
  }))

  log.addLevel('foo2', 35)
  t.is(typeof log.foo2, 'function')
  log.foo2('bar')
})

test('custom level via constructor does not affect other instances', function (t) {
  t.plan(2)

  var log = pino({level: 'foo3', levelVal: 36})
  var other = pino()
  t.is(typeof log.foo3, 'function')
  t.is(typeof other.foo3, 'undefined')
})

test('custom levels encompass higher levels', function (t) {
  t.plan(1)

  var log = pino({level: 'foo', levelVal: 35}, sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    cb()
  }))

  log.warn('bar')
})

test('after the fact add level does not include lower levels', function (t) {
  t.plan(1)

  var log = pino(sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    cb()
  }))

  log.addLevel('foo', 35)
  log.level = 'foo'
  log.info('nope')
  log.foo('bar')
})

test('children can be set to custom level', function (t) {
  t.plan(2)

  var parent = pino({level: 'foo', levelVal: 35}, sink(function (chunk, enc, cb) {
    t.is(chunk.msg, 'bar')
    t.is(chunk.child, 'yes')
    cb()
  }))
  var child = parent.child({child: 'yes'})
  child.foo('bar')
})

test('rejects already known labels', function (t) {
  t.plan(1)
  var log = pino({level: 'info', levelVal: 900})
  t.is(log.levelVal, 30)
})

test('reject already known values', function (t) {
  t.plan(1)
  try {
    pino({level: 'foo', levelVal: 30})
  } catch (e) {
    t.is(e.message.indexOf('level value') > -1, true)
  }
})
<|endoftext|>


<commit_before>/*!
 * coins.js - coins object for bcoin
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var util = require('../utils/util');
var assert = require('assert');
var constants = require('../protocol/constants');
var Coin = require('../primitives/coin');
var Output = require('../primitives/output');
var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer');
var compressor = require('./compress');
var compress = compressor.compress;
var decompress = compressor.decompress;

/**
 * Represents the outputs for a single transaction.
 * @exports Coins
 * @constructor
 * @param {TX|Object} tx/options - TX or options object.
 * @property {Hash} hash - Transaction hash.
 * @property {Number} version - Transaction version.
 * @property {Number} height - Transaction height (-1 if unconfirmed).
 * @property {Boolean} coinbase - Whether the containing
 * transaction is a coinbase.
 * @property {Coin[]} outputs - Coins.
 */

function Coins(options) {
  if (!(this instanceof Coins))
    return new Coins(options);

  this.version = 1;
  this.hash = constants.NULL_HASH;
  this.height = -1;
  this.coinbase = true;
  this.outputs = [];

  if (options)
    this.fromOptions(options);
}

/**
 * Inject properties from options object.
 * @private
 * @param {Object} options
 */

Coins.prototype.fromOptions = function fromOptions(options) {
  if (options.version != null) {
    assert(util.isNumber(options.version));
    this.version = options.version;
  }

  if (options.hash) {
    assert(typeof options.hash === 'string');
    this.hash = options.hash;
  }

  if (options.height != null) {
    assert(util.isNumber(options.height));
    this.height = options.height;
  }

  if (options.coinbase != null) {
    assert(typeof options.coinbase === 'boolean');
    this.coinbase = options.coinbase;
  }

  if (options.outputs) {
    assert(Array.isArray(options.outputs));
    this.outputs = options.outputs;
  }

  return this;
};

/**
 * Instantiate coins from options object.
 * @param {Object} options
 * @returns {Coins}
 */

Coins.fromOptions = function fromOptions(options) {
  return new Coins().fromOptions(options);
};

/**
 * Add a single output to the collection.
 * @param {Number} index
 * @param {Output} output
 */

Coins.prototype.add = function add(index, output) {
  assert(!output.script.isUnspendable());

  while (this.outputs.length <= index)
    this.outputs.push(null);

  assert(!this.outputs[index]);

  this.outputs[index] = CoinEntry.fromOutput(output);
};

/**
 * Test whether the collection has a coin.
 * @param {Number} index
 * @returns {Boolean}
 */

Coins.prototype.has = function has(index) {
  if (index >= this.outputs.length)
    return false;

  return this.outputs[index] != null;
};

/**
 * Get a coin entry.
 * @param {Number} index
 * @returns {CoinEntry}
 */

Coins.prototype.get = function get(index) {
  if (index >= this.outputs.length)
    return;

  return this.outputs[index];
};

/**
 * Get a coin.
 * @param {Number} index
 * @returns {Coin}
 */

Coins.prototype.getCoin = function getCoin(index) {
  var entry = this.get(index);

  if (!entry)
    return;

  return entry.toCoin(this, index);
};

/**
 * Remove a coin entry and return it.
 * @param {Number} index
 * @returns {CoinEntry}
 */

Coins.prototype.spend = function spend(index) {
  var entry = this.get(index);

  if (!entry)
    return;

  this.outputs[index] = null;
  this.cleanup();

  return entry;
};

/**
 * Cleanup spent outputs.
 */

Coins.prototype.cleanup = function cleanup() {
  var len = this.outputs.length;

  while (len > 0 && !this.outputs[len - 1])
    len--;

  this.outputs.length = len;
};

/**
 * Test whether the coins are fully spent.
 * @returns {Boolean}
 */

Coins.prototype.isEmpty = function isEmpty() {
  return this.outputs.length === 0;
};

/*
 * Coins serialization:
 * version: varint
 * height: uint32
 * header-code: varint (31-bit fields | 1-bit coinbase-flag)
 * spent-field: bitfield (0=spent, 1=unspent)
 * outputs (repeated):
 *   value: varint
 *   compressed-script:
 *     prefix:
 *             0x00 = 20 byte pubkey hash
 *             0x01 = 20 byte script hash
 *             0x02-0x05 = 32 byte ec-key x-value
 *             >=0x06 = varint-size + 10 | raw script
 *     data: script data, dictated by the prefix
 *
 * The compression below sacrifices some cpu in exchange
 * for reduced size, but in some cases the use of varints
 * actually increases speed (varint versions and values
 * for example). We do as much compression as possible
 * without sacrificing too much cpu. Value compression
 * is intentionally excluded for now as it seems to be
 * too much of a perf hit. Maybe when v8 optimizes
 * non-smi arithmetic better we can enable it.
 */

/**
 * Serialize the coins object.
 * @returns {Buffer}
 */

Coins.prototype.toRaw = function toRaw() {
  var bw = new BufferWriter();
  var len = this.outputs.length;
  var first = len > 0 && this.outputs[0];
  var second = len > 1 && this.outputs[1];
  var size = 0;
  var nonzero = 0;
  var i, j, code, ch, output;

  // Throw if we're fully spent.
  assert(len !== 0, 'Cannot serialize fully-spent coins.');

  // Calculate number of unspents and spent field size.
  // size = number of bytes required for the bit field.
  // nonzero = number of non-zero bytes required.
  for (i = 0; 2 + i * 8 < len; i++) {
    for (j = 0; j < 8 && 2 + i * 8 + j < len; j++) {
      if (this.outputs[2 + i * 8 + j]) {
        size = i + 1;
        nonzero++;
        break;
      }
    }
  }

  // First and second bits
  // have a double meaning.
  if (!first && !second) {
    assert(nonzero !== 0);
    nonzero -= 1;
  }

  // Calculate header code.
  code = 8 * nonzero;

  if (this.coinbase)
    code += 1;

  if (first)
    code += 2;

  if (second)
    code += 4;

  // Write headers.
  bw.writeVarint(this.version);
  bw.writeU32(this.height);
  bw.writeVarint(code);

  // Write the spent field.
  for (i = 0; i < size; i++) {
    ch = 0;
    for (j = 0; j < 8 && 2 + i * 8 + j < len; j++) {
      if (this.outputs[2 + i * 8 + j])
        ch |= 1 << j;
    }
    bw.writeU8(ch);
  }

  // Write the compressed outputs.
  for (i = 0; i < len; i++) {
    output = this.outputs[i];

    if (!output)
      continue;

    output.toWriter(bw);
  }

  return bw.render();
};

/**
 * Inject data from serialized coins.
 * @private
 * @param {Buffer} data
 * @param {Hash} hash
 * @returns {Coins}
 */

Coins.prototype.fromRaw = function fromRaw(data, hash) {
  var br = new BufferReader(data);
  var i, code, field, nonzero, ch, unspent, coin;

  this.hash = hash;

  // Read headers.
  this.version = br.readVarint();
  this.height = br.readU32();
  code = br.readVarint();
  this.coinbase = (code & 1) !== 0;

  // Setup spent field.
  field = [
    (code & 2) !== 0,
    (code & 4) !== 0
  ];

  // Recalculate number of non-zero bytes.
  nonzero = code / 8 | 0;

  if ((code & 6) === 0)
    nonzero += 1;

  // Read spent field.
  while (nonzero > 0) {
    ch = br.readU8();
    for (i = 0; i < 8; i++) {
      unspent = (ch & (1 << i)) !== 0;
      field.push(unspent);
    }
    if (ch !== 0)
      nonzero--;
  }

  // Read outputs.
  for (i = 0; i < field.length; i++) {
    if (!field[i]) {
      this.outputs.push(null);
      continue;
    }

    // Store the offset and size
    // in the compressed coin object.
    coin = CoinEntry.fromReader(br);

    this.outputs.push(coin);
  }

  this.cleanup();

  return this;
};

/**
 * Parse a single serialized coin.
 * @param {Buffer} data
 * @param {Hash} hash
 * @param {Number} index
 * @returns {Coin}
 */

Coins.parseCoin = function parseCoin(data, hash, index) {
  var br = new BufferReader(data);
  var coin = new Coin();
  var i, code, field, nonzero, ch, unspent;

  coin.hash = hash;
  coin.index = index;

  // Read headers.
  coin.version = br.readVarint();
  coin.height = br.readU32();
  code = br.readVarint();
  coin.coinbase = (code & 1) !== 0;

  // Setup spent field.
  field = [
    (code & 2) !== 0,
    (code & 4) !== 0
  ];

  // Recalculate number of non-zero bytes.
  nonzero = code / 8 | 0;

  if ((code & 6) === 0)
    nonzero += 1;

  // Read spent field.
  while (nonzero > 0 && field.length <= index) {
    ch = br.readU8();
    for (i = 0; i < 8; i++) {
      unspent = (ch & (1 << i)) !== 0;
      field.push(unspent);
    }
    if (ch !== 0)
      nonzero--;
  }

  if (field.length <= index)
    return;

  while (nonzero > 0) {
    if (br.readU8() !== 0)
      nonzero--;
  }

  // Read outputs.
  for (i = 0; i < field.length; i++) {
    if (i === index) {
      if (!field[i])
        return;

      // Read compressed output.
      decompress.coin(coin, br);

      break;
    }

    if (!field[i])
      continue;

    decompress.skip(br);
  }

  return coin;
};

/**
 * Instantiate coins from a serialized Buffer.
 * @param {Buffer} data
 * @param {Hash} hash - Transaction hash.
 * @returns {Coins}
 */

Coins.fromRaw = function fromRaw(data, hash) {
  return new Coins().fromRaw(data, hash);
};

/**
 * Inject properties from tx.
 * @private
 * @param {TX} tx
 */

Coins.prototype.fromTX = function fromTX(tx) {
  var i, output;

  this.version = tx.version;
  this.hash = tx.hash('hex');
  this.height = tx.height;
  this.coinbase = tx.isCoinbase();

  for (i = 0; i < tx.outputs.length; i++) {
    output = tx.outputs[i];

    if (output.script.isUnspendable()) {
      this.outputs.push(null);
      continue;
    }

    this.outputs.push(CoinEntry.fromOutput(output));
  }

  this.cleanup();

  return this;
};

/**
 * Instantiate a coins object from a transaction.
 * @param {TX} tx
 * @returns {Coins}
 */

Coins.fromTX = function fromTX(tx) {
  return new Coins().fromTX(tx);
};

/**
 * A compressed coin is an object which defers
 * parsing of a coin. Say there is a transaction
 * with 100 outputs. When a block comes in,
 * there may only be _one_ input in that entire
 * block which redeems an output from that
 * transaction. When parsing the Coins, there
 * is no sense to get _all_ of them into their
 * abstract form. A compressed coin is just a
 * pointer to that coin in the Coins buffer, as
 * well as a size. Parsing is done only if that
 * coin is being redeemed.
 * @constructor
 * @private
 * @param {Number} offset
 * @param {Number} size
 * @param {Buffer} raw
 */

function CoinEntry() {
  this.offset = 0;
  this.size = 0;
  this.raw = null;
  this.output = null;
}

/**
 * Instantiate a reader at the correct offset.
 * @private
 * @returns {BufferReader}
 */

CoinEntry.prototype.reader = function reader() {
  var br;

  assert(this.raw);

  br = new BufferReader(this.raw);
  br.offset = this.offset;

  return br;
};

/**
 * Parse the deferred data and return a Coin.
 * @param {Coins} coins
 * @param {Number} index
 * @returns {Coin}
 */

CoinEntry.prototype.toCoin = function toCoin(coins, index) {
  var coin = new Coin();

  // Load in all necessary properties
  // from the parent Coins object.
  coin.version = coins.version;
  coin.coinbase = coins.coinbase;
  coin.height = coins.height;
  coin.hash = coins.hash;
  coin.index = index;

  if (this.output) {
    coin.script = this.output.script;
    coin.value = this.output.value;
  } else {
    decompress.coin(coin, this.reader());
  }

  return coin;
};

/**
 * Parse the deferred data and return an Output.
 * @returns {Output}
 */

CoinEntry.prototype.toOutput = function toOutput() {
  if (this.output)
    return this.output;
  return decompress.output(new Output(), this.reader());
};

/**
 * Slice off the part of the buffer
 * relevant to this particular coin.
 */

CoinEntry.prototype.toWriter = function toWriter(bw) {
  var raw;

  if (!this.raw) {
    assert(this.output);
    compress.output(this.output, bw);
    return bw;
  }

  // If we read this coin from the db and
  // didn't use it, it's still in its
  // compressed form. Just write it back
  // as a buffer for speed.
  raw = this.raw.slice(this.offset, this.offset + this.size);

  bw.writeBytes(raw);

  return bw;
};

/**
 * Instantiate compressed coin from reader.
 * @param {BufferReader} br
 * @returns {CoinEntry}
 */

CoinEntry.fromReader = function fromReader(br) {
  var entry = new CoinEntry();
  entry.offset = br.offset;
  entry.size = decompress.skip(br);
  entry.raw = br.data;
  return entry;
};

/**
 * Instantiate compressed coin from coin.
 * @param {Output} output
 * @returns {CoinEntry}
 */

CoinEntry.fromOutput = function fromOutput(output) {
  var entry = new CoinEntry();
  entry.output = output;
  return entry;
};

/*
 * Expose
 */

module.exports = Coins;
<commit_msg>coins: no compressed non-zero bytes.<commit_after>/*!
 * coins.js - coins object for bcoin
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var util = require('../utils/util');
var assert = require('assert');
var constants = require('../protocol/constants');
var Coin = require('../primitives/coin');
var Output = require('../primitives/output');
var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer');
var compressor = require('./compress');
var compress = compressor.compress;
var decompress = compressor.decompress;

/**
 * Represents the outputs for a single transaction.
 * @exports Coins
 * @constructor
 * @param {TX|Object} tx/options - TX or options object.
 * @property {Hash} hash - Transaction hash.
 * @property {Number} version - Transaction version.
 * @property {Number} height - Transaction height (-1 if unconfirmed).
 * @property {Boolean} coinbase - Whether the containing
 * transaction is a coinbase.
 * @property {Coin[]} outputs - Coins.
 */

function Coins(options) {
  if (!(this instanceof Coins))
    return new Coins(options);

  this.version = 1;
  this.hash = constants.NULL_HASH;
  this.height = -1;
  this.coinbase = true;
  this.outputs = [];

  if (options)
    this.fromOptions(options);
}

/**
 * Inject properties from options object.
 * @private
 * @param {Object} options
 */

Coins.prototype.fromOptions = function fromOptions(options) {
  if (options.version != null) {
    assert(util.isNumber(options.version));
    this.version = options.version;
  }

  if (options.hash) {
    assert(typeof options.hash === 'string');
    this.hash = options.hash;
  }

  if (options.height != null) {
    assert(util.isNumber(options.height));
    this.height = options.height;
  }

  if (options.coinbase != null) {
    assert(typeof options.coinbase === 'boolean');
    this.coinbase = options.coinbase;
  }

  if (options.outputs) {
    assert(Array.isArray(options.outputs));
    this.outputs = options.outputs;
  }

  return this;
};

/**
 * Instantiate coins from options object.
 * @param {Object} options
 * @returns {Coins}
 */

Coins.fromOptions = function fromOptions(options) {
  return new Coins().fromOptions(options);
};

/**
 * Add a single output to the collection.
 * @param {Number} index
 * @param {Output} output
 */

Coins.prototype.add = function add(index, output) {
  assert(!output.script.isUnspendable());

  while (this.outputs.length <= index)
    this.outputs.push(null);

  assert(!this.outputs[index]);

  this.outputs[index] = CoinEntry.fromOutput(output);
};

/**
 * Test whether the collection has a coin.
 * @param {Number} index
 * @returns {Boolean}
 */

Coins.prototype.has = function has(index) {
  if (index >= this.outputs.length)
    return false;

  return this.outputs[index] != null;
};

/**
 * Get a coin entry.
 * @param {Number} index
 * @returns {CoinEntry}
 */

Coins.prototype.get = function get(index) {
  if (index >= this.outputs.length)
    return;

  return this.outputs[index];
};

/**
 * Get a coin.
 * @param {Number} index
 * @returns {Coin}
 */

Coins.prototype.getCoin = function getCoin(index) {
  var entry = this.get(index);

  if (!entry)
    return;

  return entry.toCoin(this, index);
};

/**
 * Remove a coin entry and return it.
 * @param {Number} index
 * @returns {CoinEntry}
 */

Coins.prototype.spend = function spend(index) {
  var entry = this.get(index);

  if (!entry)
    return;

  this.outputs[index] = null;
  this.cleanup();

  return entry;
};

/**
 * Cleanup spent outputs.
 */

Coins.prototype.cleanup = function cleanup() {
  var len = this.outputs.length;

  while (len > 0 && !this.outputs[len - 1])
    len--;

  this.outputs.length = len;
};

/**
 * Test whether the coins are fully spent.
 * @returns {Boolean}
 */

Coins.prototype.isEmpty = function isEmpty() {
  return this.outputs.length === 0;
};

/*
 * Coins serialization:
 * version: varint
 * height: uint32
 * header-code: varint (31-bit fields | 1-bit coinbase-flag)
 * spent-field: bitfield (0=spent, 1=unspent)
 * outputs (repeated):
 *   value: varint
 *   compressed-script:
 *     prefix:
 *             0x00 = 20 byte pubkey hash
 *             0x01 = 20 byte script hash
 *             0x02-0x05 = 32 byte ec-key x-value
 *             >=0x06 = varint-size + 10 | raw script
 *     data: script data, dictated by the prefix
 *
 * The compression below sacrifices some cpu in exchange
 * for reduced size, but in some cases the use of varints
 * actually increases speed (varint versions and values
 * for example). We do as much compression as possible
 * without sacrificing too much cpu. Value compression
 * is intentionally excluded for now as it seems to be
 * too much of a perf hit. Maybe when v8 optimizes
 * non-smi arithmetic better we can enable it.
 */

/**
 * Serialize the coins object.
 * @returns {Buffer}
 */

Coins.prototype.toRaw = function toRaw() {
  var bw = new BufferWriter();
  var len = this.outputs.length;
  var size = Math.floor((len + 5) / 8);
  var first = this.has(0);
  var second = this.has(1);
  var offset = 0;
  var i, j, code, ch, output;

  // Throw if we're fully spent.
  assert(len !== 0, 'Cannot serialize fully-spent coins.');

  // First and second bits
  // have a double meaning.
  if (!first && !second) {
    assert(size !== 0);
    offset = 1;
  }

  // Calculate header code.
  code = 8 * (size - offset);

  if (this.coinbase)
    code += 1;

  if (first)
    code += 2;

  if (second)
    code += 4;

  // Write headers.
  bw.writeVarint(this.version);
  bw.writeU32(this.height);
  bw.writeVarint(code);

  // Write the spent field.
  for (i = 0; i < size; i++) {
    ch = 0;
    for (j = 0; j < 8 && 2 + i * 8 + j < len; j++) {
      if (this.outputs[2 + i * 8 + j])
        ch |= 1 << j;
    }
    bw.writeU8(ch);
  }

  // Write the compressed outputs.
  for (i = 0; i < len; i++) {
    output = this.outputs[i];

    if (!output)
      continue;

    output.toWriter(bw);
  }

  return bw.render();
};

/**
 * Inject data from serialized coins.
 * @private
 * @param {Buffer} data
 * @param {Hash} hash
 * @returns {Coins}
 */

Coins.prototype.fromRaw = function fromRaw(data, hash) {
  var br = new BufferReader(data);
  var first = null;
  var second = null;
  var i, j, code, size, offset, ch;

  // Inject hash (passed by caller).
  this.hash = hash;

  // Read headers.
  this.version = br.readVarint();
  this.height = br.readU32();
  code = br.readVarint();
  this.coinbase = (code & 1) !== 0;

  // Recalculate number of non-zero bytes.
  size = code / 8 | 0;

  if ((code & 6) === 0)
    size += 1;

  // Setup spent field.
  offset = br.offset;
  br.seek(size);

  // Read first two outputs.
  if ((code & 2) !== 0)
    first = CoinEntry.fromReader(br);

  if ((code & 4) !== 0)
    second = CoinEntry.fromReader(br);

  this.outputs.push(first);
  this.outputs.push(second);

  // Read outputs.
  for (i = 0; i < size; i++) {
    ch = br.data[offset++];
    for (j = 0; j < 8; j++) {
      if ((ch & (1 << j)) === 0) {
        this.outputs.push(null);
        continue;
      }
      this.outputs.push(CoinEntry.fromReader(br));
    }
  }

  this.cleanup();

  return this;
};

/**
 * Parse a single serialized coin.
 * @param {Buffer} data
 * @param {Hash} hash
 * @param {Number} index
 * @returns {Coin}
 */

Coins.parseCoin = function parseCoin(data, hash, index) {
  var br = new BufferReader(data);
  var coin = new Coin();
  var i, j, code, size, offset, ch;

  // Inject outpoint (passed by caller).
  coin.hash = hash;
  coin.index = index;

  // Read headers.
  coin.version = br.readVarint();
  coin.height = br.readU32();
  code = br.readVarint();
  coin.coinbase = (code & 1) !== 0;

  // Recalculate number of non-zero bytes.
  size = code / 8 | 0;

  if ((code & 6) === 0)
    size += 1;

  if (index >= 2 + size * 8)
    return;

  // Read spent field.
  offset = br.offset;
  br.seek(size);

  for (i = 2; i <= 4; i += 2) {
    if ((code & i) !== 0) {
      if (index === 0) {
        decompress.coin(coin, br);
        return coin;
      }
      decompress.skip(br);
    } else {
      if (index === 0)
        return;
    }
    index -= 1;
  }

  for (i = 0; i < size; i++) {
    ch = br.data[offset++];
    for (j = 0; j < 8; j++) {
      if ((ch & (1 << j)) !== 0) {
        if (index === 0) {
          decompress.coin(coin, br);
          return coin;
        }
        decompress.skip(br);
      } else {
        if (index === 0)
          return;
      }
      index -= 1;
    }
  }
};

/**
 * Instantiate coins from a serialized Buffer.
 * @param {Buffer} data
 * @param {Hash} hash - Transaction hash.
 * @returns {Coins}
 */

Coins.fromRaw = function fromRaw(data, hash) {
  return new Coins().fromRaw(data, hash);
};

/**
 * Inject properties from tx.
 * @private
 * @param {TX} tx
 */

Coins.prototype.fromTX = function fromTX(tx) {
  var i, output;

  this.version = tx.version;
  this.hash = tx.hash('hex');
  this.height = tx.height;
  this.coinbase = tx.isCoinbase();

  for (i = 0; i < tx.outputs.length; i++) {
    output = tx.outputs[i];

    if (output.script.isUnspendable()) {
      this.outputs.push(null);
      continue;
    }

    this.outputs.push(CoinEntry.fromOutput(output));
  }

  this.cleanup();

  return this;
};

/**
 * Instantiate a coins object from a transaction.
 * @param {TX} tx
 * @returns {Coins}
 */

Coins.fromTX = function fromTX(tx) {
  return new Coins().fromTX(tx);
};

/**
 * A compressed coin is an object which defers
 * parsing of a coin. Say there is a transaction
 * with 100 outputs. When a block comes in,
 * there may only be _one_ input in that entire
 * block which redeems an output from that
 * transaction. When parsing the Coins, there
 * is no sense to get _all_ of them into their
 * abstract form. A compressed coin is just a
 * pointer to that coin in the Coins buffer, as
 * well as a size. Parsing is done only if that
 * coin is being redeemed.
 * @constructor
 * @private
 * @param {Number} offset
 * @param {Number} size
 * @param {Buffer} raw
 */

function CoinEntry() {
  this.offset = 0;
  this.size = 0;
  this.raw = null;
  this.output = null;
}

/**
 * Instantiate a reader at the correct offset.
 * @private
 * @returns {BufferReader}
 */

CoinEntry.prototype.reader = function reader() {
  var br;

  assert(this.raw);

  br = new BufferReader(this.raw);
  br.offset = this.offset;

  return br;
};

/**
 * Parse the deferred data and return a Coin.
 * @param {Coins} coins
 * @param {Number} index
 * @returns {Coin}
 */

CoinEntry.prototype.toCoin = function toCoin(coins, index) {
  var coin = new Coin();

  // Load in all necessary properties
  // from the parent Coins object.
  coin.version = coins.version;
  coin.coinbase = coins.coinbase;
  coin.height = coins.height;
  coin.hash = coins.hash;
  coin.index = index;

  if (this.output) {
    coin.script = this.output.script;
    coin.value = this.output.value;
  } else {
    decompress.coin(coin, this.reader());
  }

  return coin;
};

/**
 * Parse the deferred data and return an Output.
 * @returns {Output}
 */

CoinEntry.prototype.toOutput = function toOutput() {
  if (this.output)
    return this.output;
  return decompress.output(new Output(), this.reader());
};

/**
 * Slice off the part of the buffer
 * relevant to this particular coin.
 */

CoinEntry.prototype.toWriter = function toWriter(bw) {
  var raw;

  if (!this.raw) {
    assert(this.output);
    compress.output(this.output, bw);
    return bw;
  }

  // If we read this coin from the db and
  // didn't use it, it's still in its
  // compressed form. Just write it back
  // as a buffer for speed.
  raw = this.raw.slice(this.offset, this.offset + this.size);

  bw.writeBytes(raw);

  return bw;
};

/**
 * Instantiate compressed coin from reader.
 * @param {BufferReader} br
 * @returns {CoinEntry}
 */

CoinEntry.fromReader = function fromReader(br) {
  var entry = new CoinEntry();
  entry.offset = br.offset;
  entry.size = decompress.skip(br);
  entry.raw = br.data;
  return entry;
};

/**
 * Instantiate compressed coin from coin.
 * @param {Output} output
 * @returns {CoinEntry}
 */

CoinEntry.fromOutput = function fromOutput(output) {
  var entry = new CoinEntry();
  entry.output = output;
  return entry;
};

/*
 * Expose
 */

module.exports = Coins;
<|endoftext|>


<commit_before>// Copyright 2015-2018, University of Colorado Boulder

/**
 * Lightweight event & listener abstraction for a single event type.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const assertValueType = require( 'AXON/assertValueType' );
  const axon = require( 'AXON/axon' );
  const EmitterIO = require( 'AXON/EmitterIO' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object} [options]
   */
  class Emitter extends PhetioObject {
    constructor( options ) {

      options = _.extend( {

        // used to validate that you are emitting with the appropriate number/types of args, see https://github.com/phetsims/axon/issues/182
        valueTypes: [],

        // {Array.<boolean>|null} - whether or not a value type can be an optional parameter for the emit function
        areTypesOptional: null,

        tandem: Tandem.optional,
        phetioState: false,
        phetioType: EmitterIO( [] ), // subtypes can override with EmitterIO([...])
        listener: null // {function} [listener] optional listener to be added during construction.
      }, options );

      super( options );

      // @private
      this.numberOfArgs = options.valueTypes.length;

      if ( options.areTypesOptional !== null ) {
        assert && assert( Array.isArray( options.areTypesOptional ) );
        assert && assert( this.numberOfArgs === options.areTypesOptional.length, 'if types are declared as optional, all args must be' +
                                                                                'declared.' );
        for ( let i = 0; i < options.areTypesOptional.length; i++ ) {
          assert( typeof options.areTypesOptional[ i ] === 'boolean' );
        }
      }

      //@private
      this.assertEmittingValidValues = assert && function( args ) {
        !options.areTypesOptional && assert( args.length === this.numberOfArgs,
          `Emitted unexpected number of args. Expected: ${this.numberOfArgs} and received ${args.length}` );
        for ( let i = 0; i < options.valueTypes.length; i++ ) {
          let isOptional;
          if ( options.areTypesOptional ) {
            isOptional = options.areTypesOptional[ i ];
          }
          assertValueType( args[ i ], options.valueTypes[ i ], isOptional );
        }
      };

      // TODO: comment this back in once all emitter method consolidation work is complete, https://github.com/phetsims/axon/issues/182
      // if ( this.isPhetioInstrumented() ) {
      //   assert && assert( options.phetioType.parameterTypes.length === this.numberOfArgs,
      //     'phet/phet-io argument types mismatch.' );
      // }

      // @private {function[]} - the listeners that will be called on emit
      this.listeners = [];

      // @private {function[][]} - during emit() keep track of which listeners should receive events in order to manage
      //                         - removal of listeners during emit()
      this.activeListenersStack = [];

      // If an initial listener was specified, add it now
      options.listener && this.addListener( options.listener );
    }

    /**
     * Dispose an Emitter that is no longer used.  Like Property.dispose, this method checks that there are no leaked
     * listeners.
     */
    dispose() {

      // See https://github.com/phetsims/axon/issues/124
      this.listeners.length = 0;

      PhetioObject.prototype.dispose.call( this );
    }

    /**
     * Adds a listener
     * @param {function} listener
     * @public
     */
    addListener( listener ) {

      assert && assert( this.listeners.indexOf( listener ) === -1, 'Cannot add the same listener twice' );

      // If a listener is added during an emit(), we must make a copy of the current list of listeners--the newly added
      // listener will be available for the next emit() but not the one in progress.  This is to match behavior with
      // removeListener.
      this.defendListeners();

      this.listeners.push( listener );
    }

    /**
     * Removes a listener
     * @param {function} listener
     * @public
     */
    removeListener( listener ) {

      const index = this.listeners.indexOf( listener );
      assert && assert( index >= 0, 'tried to removeListener on something that wasn\'t a listener' );

      // If an emit is in progress, make a copy of the current list of listeners--the removed listener will remain in
      // the list and be called for this emit call, see #72
      this.defendListeners();

      this.listeners.splice( index, 1 );
    }

    /**
     * Removes all the listeners
     * @public
     */
    removeAllListeners() {
      while ( this.listeners.length > 0 ) {
        this.removeListener( this.listeners[ 0 ] );
      }
    }

    /**
     * If addListener/removeListener is called while emit() is in progress, we must make a defensive copy of the array
     * of listeners before changing the array, and use it for the rest of the notifications until the emit call has
     * completed.
     * @private
     */
    defendListeners() {

      for ( let i = this.activeListenersStack.length - 1; i >= 0; i-- ) {

        // Once we meet a level that was already defended, we can stop, since all previous levels are also defended
        if ( this.activeListenersStack[ i ].defended ) {
          break;
        }
        else {
          const defendedListeners = this.listeners.slice();

          // Mark copies as 'defended' so that it will use the original listeners when emit started and not the modified
          // list.
          defendedListeners.defended = true;
          this.activeListenersStack[ i ] = defendedListeners;
        }
      }
    }

    /**
     * Emits a single event.
     * This method is called many times in a simulation and must be well-optimized.
     * @params - expected parameters are based on options.valueTypes, see constructor
     * @public
     */
    emit() {

      // Get the arguments from the function in an optimizable way.
      // Copied from https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments
      // .length is just an integer, this doesn't leak
      // the arguments object itself
      let args = new Array( arguments.length );
      for ( let i = 0; i < args.length; ++i ) {
        //i is always valid index in the arguments object
        args[ i ] = arguments[ i ];
      }

      // validate the args
      this.assertEmittingValidValues && this.assertEmittingValidValues( args );

      // handle phet-io events for the emitted event
      if ( this.isPhetioInstrumented() ) {

        // Enumerate named argsObject for the data stream.
        let argsObject = {};
        for ( let i = 0; i < this.phetioType.elements.length; i++ ) {
          let element = this.phetioType.elements[ i ];
          argsObject[ element.name ] = element.type.toStateObject( arguments[ i ] );
        }
        this.phetioStartEvent( 'emitted', argsObject );
      }

      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      // Notify listeners
      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ].apply( this, args );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Emits a single event with one argument.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @public
     */
    emit1( arg0 ) {

      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', {
        args: [ this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ) ]
      } );
      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ]( arg0 );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Emits a single event with two arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @public
     */
    emit2( arg0, arg1 ) {
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 )
        ]
      } );
      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ]( arg0, arg1 );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Emits a single event with three arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @param {*} arg2
     * @public
     */
    emit3( arg0, arg1, arg2 ) {
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 ),
          this.phetioType.parameterTypes[ 2 ].toStateObject( arg2 )
        ]
      } );
      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ]( arg0, arg1, arg2 );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Checks whether a listener is registered with this Emitter
     * @param {function} listener
     * @returns {boolean}
     * @public
     */
    hasListener( listener ) {
      assert && assert( arguments.length === 1, 'Emitter.hasListener should be called with 1 argument' );
      return this.listeners.indexOf( listener ) >= 0;
    }

    /**
     * Returns true if there are any listeners.
     * @returns {boolean}
     * @public
     */
    hasListeners() {
      assert && assert( arguments.length === 0, 'Emitter.hasListeners should be called without arguments' );
      return this.listeners.length > 0;
    }

    /**
     * Returns the number of listeners.
     * @returns {number}
     * @public
     */
    getListenerCount() {
      return this.listeners.length;
    }
  }

  return axon.register( 'Emitter', Emitter );
} );<commit_msg>valueTypes assert fix, https://github.com/phetsims/axon/issues/182<commit_after>// Copyright 2015-2018, University of Colorado Boulder

/**
 * Lightweight event & listener abstraction for a single event type.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const assertValueType = require( 'AXON/assertValueType' );
  const axon = require( 'AXON/axon' );
  const EmitterIO = require( 'AXON/EmitterIO' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object} [options]
   */
  class Emitter extends PhetioObject {
    constructor( options ) {

      options = _.extend( {

        // used to validate that you are emitting with the appropriate number/types of args, see https://github.com/phetsims/axon/issues/182
        valueTypes: [],

        // {Array.<boolean>|null} - whether or not a value type can be an optional parameter for the emit function
        areTypesOptional: null,

        tandem: Tandem.optional,
        phetioState: false,
        phetioType: EmitterIO( [] ), // subtypes can override with EmitterIO([...])
        listener: null // {function} [listener] optional listener to be added during construction.
      }, options );

      super( options );

      // @private
      this.numberOfArgs = options.valueTypes.length;

      if ( options.areTypesOptional !== null ) {
        assert && assert( Array.isArray( options.areTypesOptional ) );
        assert && assert( this.numberOfArgs === options.areTypesOptional.length, 'if types are declared as optional, all args must be' +
                                                                                'declared.' );
        for ( let i = 0; i < options.areTypesOptional.length; i++ ) {
          assert && assert( typeof options.areTypesOptional[ i ] === 'boolean' );
        }
      }

      //@private
      this.assertEmittingValidValues = assert && function( args ) {
        !options.areTypesOptional && assert( args.length === this.numberOfArgs,
          `Emitted unexpected number of args. Expected: ${this.numberOfArgs} and received ${args.length}` );
        for ( let i = 0; i < options.valueTypes.length; i++ ) {
          let isOptional;
          if ( options.areTypesOptional ) {
            isOptional = options.areTypesOptional[ i ];
          }
          assertValueType( args[ i ], options.valueTypes[ i ], isOptional );
        }
      };

      // TODO: comment this back in once all emitter method consolidation work is complete, https://github.com/phetsims/axon/issues/182
      // if ( this.isPhetioInstrumented() ) {
      //   assert && assert( options.phetioType.parameterTypes.length === this.numberOfArgs,
      //     'phet/phet-io argument types mismatch.' );
      // }

      // @private {function[]} - the listeners that will be called on emit
      this.listeners = [];

      // @private {function[][]} - during emit() keep track of which listeners should receive events in order to manage
      //                         - removal of listeners during emit()
      this.activeListenersStack = [];

      // If an initial listener was specified, add it now
      options.listener && this.addListener( options.listener );
    }

    /**
     * Dispose an Emitter that is no longer used.  Like Property.dispose, this method checks that there are no leaked
     * listeners.
     */
    dispose() {

      // See https://github.com/phetsims/axon/issues/124
      this.listeners.length = 0;

      PhetioObject.prototype.dispose.call( this );
    }

    /**
     * Adds a listener
     * @param {function} listener
     * @public
     */
    addListener( listener ) {

      assert && assert( this.listeners.indexOf( listener ) === -1, 'Cannot add the same listener twice' );

      // If a listener is added during an emit(), we must make a copy of the current list of listeners--the newly added
      // listener will be available for the next emit() but not the one in progress.  This is to match behavior with
      // removeListener.
      this.defendListeners();

      this.listeners.push( listener );
    }

    /**
     * Removes a listener
     * @param {function} listener
     * @public
     */
    removeListener( listener ) {

      const index = this.listeners.indexOf( listener );
      assert && assert( index >= 0, 'tried to removeListener on something that wasn\'t a listener' );

      // If an emit is in progress, make a copy of the current list of listeners--the removed listener will remain in
      // the list and be called for this emit call, see #72
      this.defendListeners();

      this.listeners.splice( index, 1 );
    }

    /**
     * Removes all the listeners
     * @public
     */
    removeAllListeners() {
      while ( this.listeners.length > 0 ) {
        this.removeListener( this.listeners[ 0 ] );
      }
    }

    /**
     * If addListener/removeListener is called while emit() is in progress, we must make a defensive copy of the array
     * of listeners before changing the array, and use it for the rest of the notifications until the emit call has
     * completed.
     * @private
     */
    defendListeners() {

      for ( let i = this.activeListenersStack.length - 1; i >= 0; i-- ) {

        // Once we meet a level that was already defended, we can stop, since all previous levels are also defended
        if ( this.activeListenersStack[ i ].defended ) {
          break;
        }
        else {
          const defendedListeners = this.listeners.slice();

          // Mark copies as 'defended' so that it will use the original listeners when emit started and not the modified
          // list.
          defendedListeners.defended = true;
          this.activeListenersStack[ i ] = defendedListeners;
        }
      }
    }

    /**
     * Emits a single event.
     * This method is called many times in a simulation and must be well-optimized.
     * @params - expected parameters are based on options.valueTypes, see constructor
     * @public
     */
    emit() {

      // Get the arguments from the function in an optimizable way.
      // Copied from https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments
      // .length is just an integer, this doesn't leak
      // the arguments object itself
      let args = new Array( arguments.length );
      for ( let i = 0; i < args.length; ++i ) {
        //i is always valid index in the arguments object
        args[ i ] = arguments[ i ];
      }

      // validate the args
      this.assertEmittingValidValues && this.assertEmittingValidValues( args );

      // handle phet-io events for the emitted event
      if ( this.isPhetioInstrumented() ) {

        // Enumerate named argsObject for the data stream.
        let argsObject = {};
        for ( let i = 0; i < this.phetioType.elements.length; i++ ) {
          let element = this.phetioType.elements[ i ];
          argsObject[ element.name ] = element.type.toStateObject( arguments[ i ] );
        }
        this.phetioStartEvent( 'emitted', argsObject );
      }

      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      // Notify listeners
      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ].apply( this, args );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Emits a single event with one argument.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @public
     */
    emit1( arg0 ) {

      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', {
        args: [ this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ) ]
      } );
      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ]( arg0 );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Emits a single event with two arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @public
     */
    emit2( arg0, arg1 ) {
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 )
        ]
      } );
      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ]( arg0, arg1 );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Emits a single event with three arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @param {*} arg2
     * @public
     */
    emit3( arg0, arg1, arg2 ) {
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 ),
          this.phetioType.parameterTypes[ 2 ].toStateObject( arg2 )
        ]
      } );
      this.activeListenersStack.push( this.listeners );
      const lastEntry = this.activeListenersStack.length - 1;

      for ( let i = 0; i < this.activeListenersStack[ lastEntry ].length; i++ ) {
        this.activeListenersStack[ lastEntry ][ i ]( arg0, arg1, arg2 );
      }

      this.activeListenersStack.pop();
      this.isPhetioInstrumented() && this.phetioEndEvent();
    }

    /**
     * Checks whether a listener is registered with this Emitter
     * @param {function} listener
     * @returns {boolean}
     * @public
     */
    hasListener( listener ) {
      assert && assert( arguments.length === 1, 'Emitter.hasListener should be called with 1 argument' );
      return this.listeners.indexOf( listener ) >= 0;
    }

    /**
     * Returns true if there are any listeners.
     * @returns {boolean}
     * @public
     */
    hasListeners() {
      assert && assert( arguments.length === 0, 'Emitter.hasListeners should be called without arguments' );
      return this.listeners.length > 0;
    }

    /**
     * Returns the number of listeners.
     * @returns {number}
     * @public
     */
    getListenerCount() {
      return this.listeners.length;
    }
  }

  return axon.register( 'Emitter', Emitter );
} );<|endoftext|>


<commit_before>'use strict';

var
  url = require('url'),
  App = require('boomerang-app'),
  Scheduler = require('boomerang-scheduler');

require('rdf-jsonify')(rdf);

var BoomerangModule = function (config) {
  var
    self = this,
    events,
    jsonify,
    context = {'@vocab': 'https://ns.bergnet.org/boomerang#'};

  if (!('boomerang' in config)) {
    config.boomerang = {};
  }

  if (!('apps' in config.boomerang)) {
    config.boomerang.apps = [];
  }

  var getDocument = function (iri) {
    var
      documentRegEx = /(#|\/)([^#\/]*)$/,
      iriParts = documentRegEx.exec(iri);

    return iri.substr(0, iri.length - iriParts[0].length);
  };

  var startApp = function (appConfig) {
    appConfig.app = new App(events.store, appConfig.base);
    appConfig.scheduler = new Scheduler(events.store, appConfig.taskListIri);

    // add schedule listener
    events.addListener('beforeGraph', function (iri, options) {
      if (iri === getDocument(appConfig.schedulerListIri)) {
        return appConfig.scheduler.schedule(appConfig.schedulerListIri, options);
      } else {
        return Promise.resolve();
      }
    });

    // start application
    require(appConfig.path)(appConfig.app);

    return Promise.resolve();
  };

  var cleanApp = function (appConfig) {
    return appConfig.scheduler.clean(appConfig.schedulerListIri)
      .then(function () {
        return appConfig.app.clean();
      })
      .then(function () {
        return jsonify.get(appConfig.base, context);
      })
      .then(function (appGraph) {console.log(appGraph);
        if ('result' in appGraph) {
          delete appGraph.result;

          return jsonify.put(appGraph);
        } else {
          return Promise.resolve();
        }
      });
  };

  this.start = function (app) {
    config.boomerang.apps.forEach(function (appConfig) {
      startApp(appConfig);
    });

    return Promise.resolve(app);
  };

  this.initApi = function (app) {
    var initApp = function (appConfig) {
      var
        path = url.parse(appConfig.base).path;

      appConfig.resetIri = appConfig.base + 'reset';
      appConfig.schedulerListIri = appConfig.base + 'data/schedule#list';
      appConfig.taskListIri = appConfig.base + 'data/tasks#list';

      app.get(path + 'reset', function (req, res) {
        cleanApp(appConfig)
          .then(function () {
            return startApp(appConfig);
          })
          .then(function () {
            res.satusCode = 204;
            res.end();
          })
          .catch(function (error) {
            console.error(error.stack);
          });
      });

      return jsonify.put({
        '@context': context,
        '@id': appConfig.base,
        'reset': {'@id': appConfig.resetIri},
        'openTasks': {'@id': appConfig.schedulerListIri}
      });
    };

    return jsonify.put({
      '@context': context,
      '@id': config.boomerang.url,
      'hosts': config.boomerang.apps.map(function (appConfig) {
        return {'@id': appConfig.base};
      })
    })
      .then(function () {
        return Promise.all(config.boomerang.apps.map(function (appConfig) {
          return initApp(appConfig);
        }));
      })
      .then(function () {
        return Promise.resolve(app);
      });
  };

  this.init = function (app) {
    events = app.get('graphModule').events;
    jsonify = new rdf.JSONify(events.store);

    app.set('boomerangModule', self);

    return self.initApi(app)
      .then(function (app) { return self.start(app); });
  }
};

module.exports = BoomerangModule;<commit_msg>added multi-user support and app REST API<commit_after>'use strict';

var
  url = require('url'),
  App = require('boomerang-app'),
  Scheduler = require('boomerang-scheduler');

require('rdf-jsonify')(rdf);

var BoomerangModule = function (config) {
  var
    self = this,
    events,
    jsonify,
    context = {'@vocab': 'https://ns.bergnet.org/boomerang#'};

  if (!('boomerang' in config)) {
    config.boomerang = {};
  }

  if (!('apps' in config.boomerang)) {
    config.boomerang.apps = [];
  }

  /**
   * Start an app with new app utils and scheduler
   * @param appConfig
   * @returns {*}
   */
  var startApp = function (appConfig) {
    appConfig.app = new App(events.store, appConfig.base);
    appConfig.scheduler = new Scheduler(events.store, {
      scheduleBaseIri: appConfig.scheduleBaseIri,
      taskListIri: appConfig.taskListIri,
      tasksPerAgent: 10
    });

    // start application
    require(appConfig.path)(appConfig.app);

    return Promise.resolve();
  };

  /**
   * Clean the task list, scheduler and result of an app
   * @param appConfig
   * @returns {*|Promise}
   */
  var cleanApp = function (appConfig) {
    return appConfig.scheduler.clean()
      .then(function () {
        return appConfig.app.clean();
      })
      .then(function () {
        return jsonify.get(appConfig.base, context);
      })
      .then(function (appGraph) {
        if ('result' in appGraph) {
          delete appGraph.result;

          return jsonify.put(appGraph);
        } else {
          return Promise.resolve();
        }
      });
  };

  /**
   * Clean and restart an app
   * @param appConfig
   * @returns {*|Promise}
   */
  var resetApp = function (appConfig) {
    return cleanApp(appConfig)
      .then(function () { return startApp(appConfig); });
  };

  /**
   * Start all app configurations
   * @param app
   * @returns {*}
   */
  this.start = function (app) {
    config.boomerang.apps.forEach(function (appConfig) {
      startApp(appConfig);
    });

    return Promise.resolve(app);
  };

  /**
   * Init the server and all app APIs
   * @param app
   * @returns {*|Promise}
   */
  this.initApi = function (app) {
    var initApp = function (appConfig) {
      // set all app IRIs
      appConfig.releaseAssignedTasksIri = appConfig.base + 'releaseAssignedTasks';
      appConfig.resetIri = appConfig.base + 'reset';
      appConfig.scheduleBaseIri = appConfig.base + 'data/schedule';
      appConfig.taskListIri = appConfig.base + 'data/tasks#list';

      // redirect to the agent schedule list
      app.get(url.parse(appConfig.scheduleBaseIri).path, function (req, res) {
        res.redirect(appConfig.scheduler.getScheduleIri(req.session.agent));
      });

      // add schedule listener
      events.addListener('beforeGraph', function (iri, options) {
        if (iri.indexOf(appConfig.scheduleBaseIri) === 0 && iri !== appConfig.scheduleBaseIri) {
          return appConfig.scheduler.schedule(options);
        } else {
          return Promise.resolve();
        }
      });

      // release assigned tasks
      app.get(url.parse(appConfig.releaseAssignedTasksIri).path, function (req, res) {
        appConfig.scheduler.releaseAssignedTasks()
          .then(function () {
            res.satusCode = 204;
            res.end();
          })
          .catch(function (error) {
            res.statusCode = 500;
            res.end(error.stack.toString());
          });
      });

      // reset application
      app.get(url.parse(appConfig.resetIri).path, function (req, res) {
        resetApp(appConfig)
          .then(function () {
            res.satusCode = 204;
            res.end();
          })
          .catch(function (error) {
            res.statusCode = 500;
            res.end(error.stack.toString());
          });
      });

      // publish app API as Linked Data
      return jsonify.put({
        '@context': context,
        '@id': appConfig.base,
        'releaseAssignedTasks': {'@id': appConfig.releaseAssignedTasksIri},
        'reset': {'@id': appConfig.resetIri},
        'openTasks': {'@id': appConfig.scheduleBaseIri}
      });
    };

    // publish server API as Linked Data
    return jsonify.put({
      '@context': context,
      '@id': config.boomerang.url,
      'hosts': config.boomerang.apps.map(function (appConfig) {
        return {'@id': appConfig.base};
      })
    })
      .then(function () {
        return Promise.all(config.boomerang.apps.map(function (appConfig) {
          return initApp(appConfig);
        }));
      })
      .then(function () {
        return Promise.resolve(app);
      });
  };

  /**
   * Init BoomeranJS LDApp module
   * @param app
   * @returns {*|Promise}
   */
  this.init = function (app) {
    events = app.get('graphModule').events;
    jsonify = new rdf.JSONify(events.store);

    app.set('boomerangModule', self);

    return self.initApi(app)
      .then(function (app) { return self.start(app); });
  }
};

module.exports = BoomerangModule;<|endoftext|>


<commit_before>var firefox = require('selenium-webdriver/firefox');
var webdriver = require('selenium-webdriver');

var profile = new firefox.Profile();
profile.addExtension('../../build/firefox/shumway.xpi');
profile.setPreference('dom.always_stop_slow_scripts', true);
profile.setPreference('shumway.disabled', true);
profile.setPreference('shumway.environment', 'test');
profile.setPreference('shumway.whitelist', '*');
//var binary = new firefox.Binary('/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin');
var binary = new firefox.Binary('/Applications/Firefox.app/Contents/MacOS/firefox-bin');

var options = new firefox.Options().setProfile(profile);
options.setBinary(binary);

var driver = new firefox.Driver(options);
driver.manage().timeouts().setScriptTimeout(10000);

function escapeRegExp(string){
  return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

function main(info, cb) {
  var i = 0;
  var n = info.parse_result.max_ticks;
  var errors = [];
  function tick() {
    if (i++ < n) {
      setTimeout(tick);
    } else {
      if (errors.length) {
        cb({ err: errors, val: null });
      } else {
        cb({err: null, val: true});
      }
      return;
    }
    try {
      document.plugin.__tick__();
    } catch (e) {
      errors.push(e.toString());
    }
  }
  tick();
}

exports.run = function (info, cb) {
  var uri = 'http://areweflashyet.com/swfs/' + info.file;
    // 'data:text/html,<!DOCTYPE html>' +
    // info.outerHTML.replace(new RegExp(escapeRegExp(info.src), 'g'), 'http://areweflashyet.com/swfs/' + info.file);
  // driver.manage().window().setSize(info.parse_result.width, info.parse_result.height);
  driver.get(uri).then(function () {
    driver.wait(function() {
      return driver.executeScript('return !!document.plugin.__tick__')
      .then(function(val) {
        return val;
      });
    }, 5000).then(function () {
      driver.executeAsyncScript(main, info)
      .then(function (result) {
        if (cb) {
          cb(result.err, result.val);
        }
      }, function (err) {
        if (cb) {
          cb(err, null);
        }
      });
    }, function (err) {
      if (cb) {
        cb(err, null);
      }
    });
  });
};
<commit_msg>Trim timeouts and max frame number<commit_after>var firefox = require('selenium-webdriver/firefox');
var webdriver = require('selenium-webdriver');

var profile = new firefox.Profile();
profile.addExtension('../../build/firefox/shumway.xpi');
profile.setPreference('dom.always_stop_slow_scripts', true);
profile.setPreference('shumway.disabled', true);
profile.setPreference('shumway.environment', 'test');
profile.setPreference('shumway.whitelist', '*');
//var binary = new firefox.Binary('/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin');
var binary = new firefox.Binary('/Applications/Firefox.app/Contents/MacOS/firefox-bin');

var options = new firefox.Options().setProfile(profile);
options.setBinary(binary);

var driver;

function escapeRegExp(string){
  return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

function main(info, cb) {
  var i = 0;
  var n = Math.min(info.parse_result.max_ticks, 100);
  var errors = [];
  function tick() {
    if (i++ < n) {
      setTimeout(tick);
    } else {
      if (errors.length) {
        cb({ err: errors, val: null });
      } else {
        cb({err: null, val: true});
      }
      return;
    }
    try {
      document.plugin.__tick__();
    } catch (e) {
      errors.push(e.toString());
    }
  }
  tick();
}

exports.run = function (info, cb) {
  var uri = 'http://areweflashyet.com/swfs/' + info.file;
    // 'data:text/html,<!DOCTYPE html>' +
    // info.outerHTML.replace(new RegExp(escapeRegExp(info.src), 'g'), 'http://areweflashyet.com/swfs/' + info.file);
  if (!driver) {
    driver = new firefox.Driver(options);
    driver.manage().timeouts().setScriptTimeout(10000);
  }
  // driver.manage().window().setSize(info.parse_result.width, info.parse_result.height);
  driver.get(uri).then(function () {
    driver.wait(function() {
      return driver.executeScript('return !!document.plugin.__tick__')
      .then(function(val) {
        return val;
      });
    }, 10000).then(function () {
      driver.executeAsyncScript(main, info)
      .then(function (result) {
        if (cb) {
          cb(result.err, result.val);
        }
      }, function (err) {
        if (cb) {
          cb(err, null);
        }
      });
    }, function (err) {
      if (cb) {
        cb(err, null);
      }
    });
  });
};
<|endoftext|>